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.
- package/README.md +3 -2
- package/fesm2022/ngx-dynamic-entity-builder.mjs +421 -116
- package/fesm2022/ngx-dynamic-entity-builder.mjs.map +1 -1
- package/package.json +7 -7
- package/types/ngx-dynamic-entity-builder.d.ts +523 -0
- package/esm2022/lib/builder-store.service.mjs +0 -1057
- package/esm2022/lib/clone.mjs +0 -14
- package/esm2022/lib/components/entity-builder-canvas.component.mjs +0 -83
- package/esm2022/lib/components/entity-builder-tree-node.component.mjs +0 -191
- package/esm2022/lib/components/entity-reference-config.component.mjs +0 -405
- package/esm2022/lib/components/field-inspector.component.mjs +0 -139
- package/esm2022/lib/components/field-palette.component.mjs +0 -57
- package/esm2022/lib/components/field-rules-list.component.mjs +0 -224
- package/esm2022/lib/components/referenced-field-config.component.mjs +0 -234
- package/esm2022/lib/components/rule-form.component.mjs +0 -201
- package/esm2022/lib/components/tab-manager.component.mjs +0 -354
- package/esm2022/lib/entity-builder.component.mjs +0 -176
- package/esm2022/lib/field-catalog.mjs +0 -6
- package/esm2022/ngx-dynamic-entity-builder.mjs +0 -5
- package/esm2022/public-api.mjs +0 -16
- package/index.d.ts +0 -5
- package/lib/builder-store.service.d.ts +0 -206
- package/lib/clone.d.ts +0 -9
- package/lib/components/entity-builder-canvas.component.d.ts +0 -9
- package/lib/components/entity-builder-tree-node.component.d.ts +0 -17
- package/lib/components/entity-reference-config.component.d.ts +0 -29
- package/lib/components/field-inspector.component.d.ts +0 -33
- package/lib/components/field-palette.component.d.ts +0 -13
- package/lib/components/field-rules-list.component.d.ts +0 -19
- package/lib/components/referenced-field-config.component.d.ts +0 -24
- package/lib/components/rule-form.component.d.ts +0 -16
- package/lib/components/tab-manager.component.d.ts +0 -28
- package/lib/entity-builder.component.d.ts +0 -53
- package/lib/field-catalog.d.ts +0 -5
- package/public-api.d.ts +0 -12
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { Component, computed, inject } from '@angular/core';
|
|
2
|
-
import { FormsModule } from '@angular/forms';
|
|
3
|
-
import { MatButtonModule } from '@angular/material/button';
|
|
4
|
-
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
5
|
-
import { MatDividerModule } from '@angular/material/divider';
|
|
6
|
-
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
7
|
-
import { MatIconModule } from '@angular/material/icon';
|
|
8
|
-
import { MatInputModule } from '@angular/material/input';
|
|
9
|
-
import { MatSelectModule } from '@angular/material/select';
|
|
10
|
-
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|
11
|
-
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
12
|
-
import { resolveLabel, resolveOptionLabel } from '@dynamic-entity/core';
|
|
13
|
-
import { BuilderStore } from '../builder-store.service';
|
|
14
|
-
import { getFieldTypeMeta } from '../field-catalog';
|
|
15
|
-
import { EntityReferenceConfigComponent } from './entity-reference-config.component';
|
|
16
|
-
import { FieldRulesListComponent } from './field-rules-list.component';
|
|
17
|
-
import { ReferencedFieldConfigComponent } from './referenced-field-config.component';
|
|
18
|
-
import * as i0 from "@angular/core";
|
|
19
|
-
import * as i1 from "@angular/forms";
|
|
20
|
-
import * as i2 from "@angular/material/button";
|
|
21
|
-
import * as i3 from "@angular/material/checkbox";
|
|
22
|
-
import * as i4 from "@angular/material/divider";
|
|
23
|
-
import * as i5 from "@angular/material/form-field";
|
|
24
|
-
import * as i6 from "@angular/material/icon";
|
|
25
|
-
import * as i7 from "@angular/material/input";
|
|
26
|
-
import * as i8 from "@angular/material/select";
|
|
27
|
-
import * as i9 from "@angular/material/core";
|
|
28
|
-
import * as i10 from "@angular/material/slide-toggle";
|
|
29
|
-
import * as i11 from "@angular/material/tooltip";
|
|
30
|
-
/**
|
|
31
|
-
* FieldInspectorComponent — edits every property of the currently selected field.
|
|
32
|
-
*/
|
|
33
|
-
export class FieldInspectorComponent {
|
|
34
|
-
store = inject(BuilderStore);
|
|
35
|
-
field = this.store.selectedField;
|
|
36
|
-
meta = computed(() => {
|
|
37
|
-
const f = this.field();
|
|
38
|
-
return f ? getFieldTypeMeta(f.type) : undefined;
|
|
39
|
-
});
|
|
40
|
-
lang() {
|
|
41
|
-
return this.store.activeLanguage();
|
|
42
|
-
}
|
|
43
|
-
toNum(value) {
|
|
44
|
-
if (value === '' || value === null || value === undefined)
|
|
45
|
-
return null;
|
|
46
|
-
const n = Number(value);
|
|
47
|
-
return Number.isNaN(n) ? null : n;
|
|
48
|
-
}
|
|
49
|
-
labelValue(field) {
|
|
50
|
-
return resolveLabel(field.label, this.lang());
|
|
51
|
-
}
|
|
52
|
-
placeholderValue(field) {
|
|
53
|
-
return resolveLabel(field.placeholder, this.lang());
|
|
54
|
-
}
|
|
55
|
-
optionLabel(option) {
|
|
56
|
-
return resolveOptionLabel(option, this.lang());
|
|
57
|
-
}
|
|
58
|
-
// ─── showWhen editing ───────────────────────────────────────────────────────
|
|
59
|
-
showWhenEntries(field) {
|
|
60
|
-
return Object.entries(field.showWhen ?? {}).map(([key, value]) => ({
|
|
61
|
-
key,
|
|
62
|
-
display: this.stringifyShowWhen(value),
|
|
63
|
-
}));
|
|
64
|
-
}
|
|
65
|
-
addShowWhen(field) {
|
|
66
|
-
const next = { ...(field.showWhen ?? {}) };
|
|
67
|
-
let key = 'field';
|
|
68
|
-
let n = 1;
|
|
69
|
-
while (key in next)
|
|
70
|
-
key = `field_${++n}`;
|
|
71
|
-
next[key] = true;
|
|
72
|
-
this.store.setShowWhen(field.id, next);
|
|
73
|
-
}
|
|
74
|
-
renameShowWhen(field, oldKey, newKey) {
|
|
75
|
-
const trimmed = newKey.trim();
|
|
76
|
-
if (!trimmed || trimmed === oldKey)
|
|
77
|
-
return;
|
|
78
|
-
const current = field.showWhen ?? {};
|
|
79
|
-
if (trimmed in current)
|
|
80
|
-
return;
|
|
81
|
-
const next = {};
|
|
82
|
-
// Rebuild in order so the row does not jump while the user is typing.
|
|
83
|
-
for (const [k, v] of Object.entries(current))
|
|
84
|
-
next[k === oldKey ? trimmed : k] = v;
|
|
85
|
-
this.store.setShowWhen(field.id, next);
|
|
86
|
-
}
|
|
87
|
-
setShowWhenValue(field, key, raw) {
|
|
88
|
-
this.store.setShowWhen(field.id, { ...(field.showWhen ?? {}), [key]: this.parseShowWhen(raw) });
|
|
89
|
-
}
|
|
90
|
-
removeShowWhen(field, key) {
|
|
91
|
-
const next = { ...(field.showWhen ?? {}) };
|
|
92
|
-
delete next[key];
|
|
93
|
-
this.store.setShowWhen(field.id, next);
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* `showWhen` compares with `!==`, so the authored type has to survive the round trip:
|
|
97
|
-
* "true"/"false" become booleans and numeric text becomes a number.
|
|
98
|
-
*/
|
|
99
|
-
parseShowWhen(raw) {
|
|
100
|
-
const trimmed = raw.trim();
|
|
101
|
-
if (trimmed === 'true')
|
|
102
|
-
return true;
|
|
103
|
-
if (trimmed === 'false')
|
|
104
|
-
return false;
|
|
105
|
-
if (trimmed === 'null')
|
|
106
|
-
return null;
|
|
107
|
-
if (trimmed !== '' && !Number.isNaN(Number(trimmed)))
|
|
108
|
-
return Number(trimmed);
|
|
109
|
-
return raw;
|
|
110
|
-
}
|
|
111
|
-
stringifyShowWhen(value) {
|
|
112
|
-
if (value === null)
|
|
113
|
-
return 'null';
|
|
114
|
-
if (typeof value === 'object')
|
|
115
|
-
return JSON.stringify(value);
|
|
116
|
-
return String(value);
|
|
117
|
-
}
|
|
118
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FieldInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
119
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: FieldInspectorComponent, isStandalone: true, selector: "ngx-field-inspector", ngImport: i0, template: "@if (field(); as f) {\n <div class=\"deb-inspector\">\n <!-- Identity -->\n <div class=\"deb-row\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>Field id</mat-label>\n <input matInput data-testid=\"field-id\" [value]=\"f.id\" disabled />\n @if (store.hasManualId(f.id)) {\n <mat-hint>The stored data key for this saved field \u2014 fixed.</mat-hint>\n } @else {\n <mat-hint>Derived from the label. Used as the data key.</mat-hint>\n }\n </mat-form-field>\n <span class=\"deb-chip\">{{ meta()?.label || f.type }}</span>\n </div>\n\n <!-- Localised text (active language) -->\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\n <mat-label>Label ({{ lang() }})</mat-label>\n <input\n matInput\n [ngModel]=\"labelValue(f)\"\n (ngModelChange)=\"store.setFieldLabel(f.id, lang(), $event)\"\n />\n </mat-form-field>\n\n @if (meta()?.supportsPlaceholder) {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\n <mat-label>Placeholder ({{ lang() }})</mat-label>\n <input\n matInput\n [ngModel]=\"placeholderValue(f)\"\n (ngModelChange)=\"store.setFieldPlaceholder(f.id, lang(), $event)\"\n />\n </mat-form-field>\n }\n\n @if (meta()?.supportsDefaultValue) {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\n <mat-label>Default value</mat-label>\n <input\n matInput\n [ngModel]=\"f.defaultValue ?? ''\"\n (ngModelChange)=\"store.updateField(f.id, { defaultValue: $event })\"\n />\n </mat-form-field>\n }\n\n <!-- Entity reference, cascade, and autoPatch -->\n @if (meta()?.isEntityRef) {\n <mat-divider></mat-divider>\n <ngx-entity-reference-config />\n }\n\n <mat-divider></mat-divider>\n\n <!-- Validators -->\n @if (meta(); as m) {\n @if (m.flagValidators.length || m.paramValidators.length) {\n <span class=\"deb-section-title\">Validation</span>\n <div class=\"deb-toggles\">\n @for (v of m.flagValidators; track v) {\n <mat-checkbox\n [ngModel]=\"store.hasFlagValidator(f, v)\"\n (ngModelChange)=\"store.toggleFlagValidator(f.id, v, $event)\"\n >{{ v }}</mat-checkbox>\n }\n </div>\n @for (v of m.paramValidators; track v) {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\n <mat-label>{{ v }}</mat-label>\n <input\n matInput\n type=\"number\"\n [ngModel]=\"store.getParamValidator(f, v)\"\n (ngModelChange)=\"store.setParamValidator(f.id, v, toNum($event))\"\n />\n </mat-form-field>\n }\n }\n }\n\n <!-- Options (dropdown / radio / multiSelect) -->\n @if (meta()?.hasOptions) {\n <mat-divider></mat-divider>\n <span class=\"deb-section-title\">Data source</span>\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\n <mat-label>Options come from</mat-label>\n <mat-select\n data-testid=\"data-source\"\n [ngModel]=\"store.fieldDataSource(f)\"\n (ngModelChange)=\"store.setFieldDataSource(f.id, $event)\"\n >\n <mat-option value=\"none\">Nothing yet</mat-option>\n <mat-option value=\"manual\">Options authored here</mat-option>\n <mat-option value=\"lookup\">A named list</mat-option>\n </mat-select>\n </mat-form-field>\n }\n\n <!-- Named lookup list -->\n @if (meta()?.hasOptions && store.fieldDataSource(f) === 'lookup') {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\n <mat-label>List name</mat-label>\n <input\n matInput\n data-testid=\"list-name\"\n [ngModel]=\"f.listName ?? ''\"\n (ngModelChange)=\"store.setListName(f.id, $event)\"\n />\n </mat-form-field>\n <p class=\"deb-hint\">\n The consuming app resolves this name through its lookup registry. Options are not\n authored here.\n </p>\n }\n\n <!-- Inline options -->\n @if (meta()?.hasOptions && store.fieldDataSource(f) !== 'lookup') {\n <div class=\"deb-row\" style=\"justify-content: space-between;\">\n <span class=\"deb-section-title\">Options</span>\n <button mat-stroked-button type=\"button\" (click)=\"store.addOption(f.id)\">\n <mat-icon>add</mat-icon> Option\n </button>\n </div>\n @for (opt of f.options ?? []; track $index) {\n <div class=\"deb-option-row\" data-testid=\"option-row\">\n <!-- One input: the displayed text is the stored value. -->\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\n <mat-label>Option ({{ lang() }})</mat-label>\n <input\n matInput\n [attr.data-testid]=\"'option-' + $index\"\n [ngModel]=\"optionLabel(opt)\"\n (ngModelChange)=\"store.setOptionLabel(f.id, $index, lang(), $event)\"\n />\n </mat-form-field>\n <button mat-icon-button type=\"button\" color=\"warn\"\n (click)=\"store.removeOption(f.id, $index)\" matTooltip=\"Remove option\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n }\n @if (!(f.options ?? []).length) {\n <p class=\"deb-hint\">No options yet \u2014 add at least one.</p>\n }\n }\n\n <mat-divider></mat-divider>\n\n <!-- Flags -->\n <span class=\"deb-section-title\">Display</span>\n <div class=\"deb-toggles\">\n <mat-slide-toggle\n [ngModel]=\"f.visibility ?? true\"\n (ngModelChange)=\"store.updateField(f.id, { visibility: $event })\"\n >Visible</mat-slide-toggle>\n <mat-slide-toggle\n [ngModel]=\"f.table?.visible ?? false\"\n (ngModelChange)=\"store.updateField(f.id, { table: { visible: $event } })\"\n >Table column</mat-slide-toggle>\n <mat-slide-toggle\n [ngModel]=\"f.readonly ?? false\"\n (ngModelChange)=\"store.updateField(f.id, { readonly: $event })\"\n >Read-only</mat-slide-toggle>\n <mat-slide-toggle\n [ngModel]=\"f.disabled ?? false\"\n (ngModelChange)=\"store.updateField(f.id, { disabled: $event })\"\n >Disabled</mat-slide-toggle>\n <mat-slide-toggle\n [ngModel]=\"f.maskData ?? false\"\n (ngModelChange)=\"store.updateField(f.id, { maskData: $event })\"\n >Mask data (RBAC)</mat-slide-toggle>\n <mat-slide-toggle\n data-testid=\"toggle-critical\"\n [ngModel]=\"f.criticalField ?? false\"\n (ngModelChange)=\"store.updateField(f.id, { criticalField: $event })\"\n matTooltip=\"Renders locked; editing it announces a change against the session baseline.\"\n >Critical field</mat-slide-toggle>\n <mat-slide-toggle\n [ngModel]=\"f.showOnMinimize ?? false\"\n (ngModelChange)=\"store.updateField(f.id, { showOnMinimize: $event })\"\n matTooltip=\"Shown in the record editor's summary panel.\"\n >Show in summary</mat-slide-toggle>\n </div>\n\n <mat-divider></mat-divider>\n\n <!-- Conditional visibility (static showWhen) -->\n <div class=\"deb-row deb-row--split\">\n <span class=\"deb-section-title\">Show when</span>\n <button mat-stroked-button type=\"button\" data-testid=\"add-show-when\" (click)=\"addShowWhen(f)\">\n <mat-icon>add</mat-icon> Condition\n </button>\n </div>\n @for (entry of showWhenEntries(f); track entry.key) {\n <div class=\"deb-option-row\" data-testid=\"option-row\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>Field id</mat-label>\n <input matInput [ngModel]=\"entry.key\" (ngModelChange)=\"renameShowWhen(f, entry.key, $event)\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>Equals</mat-label>\n <input matInput [ngModel]=\"entry.display\" (ngModelChange)=\"setShowWhenValue(f, entry.key, $event)\" />\n </mat-form-field>\n <button mat-icon-button type=\"button\" color=\"warn\" matTooltip=\"Remove condition\"\n (click)=\"removeShowWhen(f, entry.key)\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n }\n @if (!showWhenEntries(f).length) {\n <p class=\"deb-hint\">Always visible. Add a condition to show this field only for certain values.</p>\n }\n\n <!-- patchOnTrue (boolean/checkbox only) -->\n @if (f.type === 'boolean' || f.type === 'checkbox') {\n <mat-divider></mat-divider>\n <div class=\"deb-row deb-row--split\">\n <span class=\"deb-section-title\">Copy on true</span>\n <button mat-stroked-button type=\"button\" data-testid=\"add-patch-on-true\"\n (click)=\"store.addPatchOnTrueMapping(f.id)\">\n <mat-icon>add</mat-icon> Mapping\n </button>\n </div>\n @for (mapping of f.patchOnTrue ?? []; track $index) {\n <div class=\"deb-option-row\" data-testid=\"option-row\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>From field</mat-label>\n <input matInput [ngModel]=\"mapping.from\"\n (ngModelChange)=\"store.updatePatchOnTrueMapping(f.id, $index, { from: $event })\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>To field</mat-label>\n <input matInput [ngModel]=\"mapping.to\"\n (ngModelChange)=\"store.updatePatchOnTrueMapping(f.id, $index, { to: $event })\" />\n </mat-form-field>\n <button mat-icon-button type=\"button\" color=\"warn\" matTooltip=\"Remove mapping\"\n (click)=\"store.removePatchOnTrueMapping(f.id, $index)\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n }\n @if (!(f.patchOnTrue ?? []).length) {\n <p class=\"deb-hint\">When this flips to true, copy one field's value into another.</p>\n }\n }\n\n <mat-divider></mat-divider>\n\n <!-- Rules attached to this field -->\n <ngx-field-rules-list />\n\n <mat-divider></mat-divider>\n\n <!-- Cross-entity field reference & drift management (Phase 8) -->\n <ngx-referenced-field-config />\n </div>\n} @else {\n <div class=\"deb-inspector__empty\">\n <mat-icon>touch_app</mat-icon>\n <p>Select a field to edit its properties.</p>\n </div>\n}\n", styles: [".deb-inspector{display:flex;flex-direction:column;gap:12px}.deb-inspector__empty{color:var(--deb-muted, #6b7280);text-align:center;padding:24px 8px}.deb-full{width:100%}.deb-row{display:flex;gap:8px;align-items:center}.deb-row>*{flex:1}.deb-row--split{justify-content:space-between}.deb-row--split>*{flex:0 0 auto}.deb-toggles{display:grid;grid-template-columns:1fr 1fr;gap:8px 12px}.deb-option-row{display:flex;gap:6px;align-items:center}.deb-chip{display:inline-block;padding:2px 8px;border-radius:12px;background:var(--deb-accent-soft, #eef2ff);color:var(--deb-accent, #6366f1);font-size:12px;font-weight:600}\n"], dependencies: [{ 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i3.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i4.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "directive", type: i5.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.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 }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i9.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i10.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i11.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: EntityReferenceConfigComponent, selector: "ngx-entity-reference-config" }, { kind: "component", type: FieldRulesListComponent, selector: "ngx-field-rules-list" }, { kind: "component", type: ReferencedFieldConfigComponent, selector: "ngx-referenced-field-config" }] });
|
|
120
|
-
}
|
|
121
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FieldInspectorComponent, decorators: [{
|
|
122
|
-
type: Component,
|
|
123
|
-
args: [{ selector: 'ngx-field-inspector', standalone: true, imports: [
|
|
124
|
-
FormsModule,
|
|
125
|
-
MatButtonModule,
|
|
126
|
-
MatCheckboxModule,
|
|
127
|
-
MatDividerModule,
|
|
128
|
-
MatFormFieldModule,
|
|
129
|
-
MatIconModule,
|
|
130
|
-
MatInputModule,
|
|
131
|
-
MatSelectModule,
|
|
132
|
-
MatSlideToggleModule,
|
|
133
|
-
MatTooltipModule,
|
|
134
|
-
EntityReferenceConfigComponent,
|
|
135
|
-
FieldRulesListComponent,
|
|
136
|
-
ReferencedFieldConfigComponent,
|
|
137
|
-
], template: "@if (field(); as f) {\n <div class=\"deb-inspector\">\n <!-- Identity -->\n <div class=\"deb-row\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>Field id</mat-label>\n <input matInput data-testid=\"field-id\" [value]=\"f.id\" disabled />\n @if (store.hasManualId(f.id)) {\n <mat-hint>The stored data key for this saved field \u2014 fixed.</mat-hint>\n } @else {\n <mat-hint>Derived from the label. Used as the data key.</mat-hint>\n }\n </mat-form-field>\n <span class=\"deb-chip\">{{ meta()?.label || f.type }}</span>\n </div>\n\n <!-- Localised text (active language) -->\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\n <mat-label>Label ({{ lang() }})</mat-label>\n <input\n matInput\n [ngModel]=\"labelValue(f)\"\n (ngModelChange)=\"store.setFieldLabel(f.id, lang(), $event)\"\n />\n </mat-form-field>\n\n @if (meta()?.supportsPlaceholder) {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\n <mat-label>Placeholder ({{ lang() }})</mat-label>\n <input\n matInput\n [ngModel]=\"placeholderValue(f)\"\n (ngModelChange)=\"store.setFieldPlaceholder(f.id, lang(), $event)\"\n />\n </mat-form-field>\n }\n\n @if (meta()?.supportsDefaultValue) {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\n <mat-label>Default value</mat-label>\n <input\n matInput\n [ngModel]=\"f.defaultValue ?? ''\"\n (ngModelChange)=\"store.updateField(f.id, { defaultValue: $event })\"\n />\n </mat-form-field>\n }\n\n <!-- Entity reference, cascade, and autoPatch -->\n @if (meta()?.isEntityRef) {\n <mat-divider></mat-divider>\n <ngx-entity-reference-config />\n }\n\n <mat-divider></mat-divider>\n\n <!-- Validators -->\n @if (meta(); as m) {\n @if (m.flagValidators.length || m.paramValidators.length) {\n <span class=\"deb-section-title\">Validation</span>\n <div class=\"deb-toggles\">\n @for (v of m.flagValidators; track v) {\n <mat-checkbox\n [ngModel]=\"store.hasFlagValidator(f, v)\"\n (ngModelChange)=\"store.toggleFlagValidator(f.id, v, $event)\"\n >{{ v }}</mat-checkbox>\n }\n </div>\n @for (v of m.paramValidators; track v) {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\n <mat-label>{{ v }}</mat-label>\n <input\n matInput\n type=\"number\"\n [ngModel]=\"store.getParamValidator(f, v)\"\n (ngModelChange)=\"store.setParamValidator(f.id, v, toNum($event))\"\n />\n </mat-form-field>\n }\n }\n }\n\n <!-- Options (dropdown / radio / multiSelect) -->\n @if (meta()?.hasOptions) {\n <mat-divider></mat-divider>\n <span class=\"deb-section-title\">Data source</span>\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\n <mat-label>Options come from</mat-label>\n <mat-select\n data-testid=\"data-source\"\n [ngModel]=\"store.fieldDataSource(f)\"\n (ngModelChange)=\"store.setFieldDataSource(f.id, $event)\"\n >\n <mat-option value=\"none\">Nothing yet</mat-option>\n <mat-option value=\"manual\">Options authored here</mat-option>\n <mat-option value=\"lookup\">A named list</mat-option>\n </mat-select>\n </mat-form-field>\n }\n\n <!-- Named lookup list -->\n @if (meta()?.hasOptions && store.fieldDataSource(f) === 'lookup') {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\n <mat-label>List name</mat-label>\n <input\n matInput\n data-testid=\"list-name\"\n [ngModel]=\"f.listName ?? ''\"\n (ngModelChange)=\"store.setListName(f.id, $event)\"\n />\n </mat-form-field>\n <p class=\"deb-hint\">\n The consuming app resolves this name through its lookup registry. Options are not\n authored here.\n </p>\n }\n\n <!-- Inline options -->\n @if (meta()?.hasOptions && store.fieldDataSource(f) !== 'lookup') {\n <div class=\"deb-row\" style=\"justify-content: space-between;\">\n <span class=\"deb-section-title\">Options</span>\n <button mat-stroked-button type=\"button\" (click)=\"store.addOption(f.id)\">\n <mat-icon>add</mat-icon> Option\n </button>\n </div>\n @for (opt of f.options ?? []; track $index) {\n <div class=\"deb-option-row\" data-testid=\"option-row\">\n <!-- One input: the displayed text is the stored value. -->\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\n <mat-label>Option ({{ lang() }})</mat-label>\n <input\n matInput\n [attr.data-testid]=\"'option-' + $index\"\n [ngModel]=\"optionLabel(opt)\"\n (ngModelChange)=\"store.setOptionLabel(f.id, $index, lang(), $event)\"\n />\n </mat-form-field>\n <button mat-icon-button type=\"button\" color=\"warn\"\n (click)=\"store.removeOption(f.id, $index)\" matTooltip=\"Remove option\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n }\n @if (!(f.options ?? []).length) {\n <p class=\"deb-hint\">No options yet \u2014 add at least one.</p>\n }\n }\n\n <mat-divider></mat-divider>\n\n <!-- Flags -->\n <span class=\"deb-section-title\">Display</span>\n <div class=\"deb-toggles\">\n <mat-slide-toggle\n [ngModel]=\"f.visibility ?? true\"\n (ngModelChange)=\"store.updateField(f.id, { visibility: $event })\"\n >Visible</mat-slide-toggle>\n <mat-slide-toggle\n [ngModel]=\"f.table?.visible ?? false\"\n (ngModelChange)=\"store.updateField(f.id, { table: { visible: $event } })\"\n >Table column</mat-slide-toggle>\n <mat-slide-toggle\n [ngModel]=\"f.readonly ?? false\"\n (ngModelChange)=\"store.updateField(f.id, { readonly: $event })\"\n >Read-only</mat-slide-toggle>\n <mat-slide-toggle\n [ngModel]=\"f.disabled ?? false\"\n (ngModelChange)=\"store.updateField(f.id, { disabled: $event })\"\n >Disabled</mat-slide-toggle>\n <mat-slide-toggle\n [ngModel]=\"f.maskData ?? false\"\n (ngModelChange)=\"store.updateField(f.id, { maskData: $event })\"\n >Mask data (RBAC)</mat-slide-toggle>\n <mat-slide-toggle\n data-testid=\"toggle-critical\"\n [ngModel]=\"f.criticalField ?? false\"\n (ngModelChange)=\"store.updateField(f.id, { criticalField: $event })\"\n matTooltip=\"Renders locked; editing it announces a change against the session baseline.\"\n >Critical field</mat-slide-toggle>\n <mat-slide-toggle\n [ngModel]=\"f.showOnMinimize ?? false\"\n (ngModelChange)=\"store.updateField(f.id, { showOnMinimize: $event })\"\n matTooltip=\"Shown in the record editor's summary panel.\"\n >Show in summary</mat-slide-toggle>\n </div>\n\n <mat-divider></mat-divider>\n\n <!-- Conditional visibility (static showWhen) -->\n <div class=\"deb-row deb-row--split\">\n <span class=\"deb-section-title\">Show when</span>\n <button mat-stroked-button type=\"button\" data-testid=\"add-show-when\" (click)=\"addShowWhen(f)\">\n <mat-icon>add</mat-icon> Condition\n </button>\n </div>\n @for (entry of showWhenEntries(f); track entry.key) {\n <div class=\"deb-option-row\" data-testid=\"option-row\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>Field id</mat-label>\n <input matInput [ngModel]=\"entry.key\" (ngModelChange)=\"renameShowWhen(f, entry.key, $event)\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>Equals</mat-label>\n <input matInput [ngModel]=\"entry.display\" (ngModelChange)=\"setShowWhenValue(f, entry.key, $event)\" />\n </mat-form-field>\n <button mat-icon-button type=\"button\" color=\"warn\" matTooltip=\"Remove condition\"\n (click)=\"removeShowWhen(f, entry.key)\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n }\n @if (!showWhenEntries(f).length) {\n <p class=\"deb-hint\">Always visible. Add a condition to show this field only for certain values.</p>\n }\n\n <!-- patchOnTrue (boolean/checkbox only) -->\n @if (f.type === 'boolean' || f.type === 'checkbox') {\n <mat-divider></mat-divider>\n <div class=\"deb-row deb-row--split\">\n <span class=\"deb-section-title\">Copy on true</span>\n <button mat-stroked-button type=\"button\" data-testid=\"add-patch-on-true\"\n (click)=\"store.addPatchOnTrueMapping(f.id)\">\n <mat-icon>add</mat-icon> Mapping\n </button>\n </div>\n @for (mapping of f.patchOnTrue ?? []; track $index) {\n <div class=\"deb-option-row\" data-testid=\"option-row\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>From field</mat-label>\n <input matInput [ngModel]=\"mapping.from\"\n (ngModelChange)=\"store.updatePatchOnTrueMapping(f.id, $index, { from: $event })\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>To field</mat-label>\n <input matInput [ngModel]=\"mapping.to\"\n (ngModelChange)=\"store.updatePatchOnTrueMapping(f.id, $index, { to: $event })\" />\n </mat-form-field>\n <button mat-icon-button type=\"button\" color=\"warn\" matTooltip=\"Remove mapping\"\n (click)=\"store.removePatchOnTrueMapping(f.id, $index)\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n }\n @if (!(f.patchOnTrue ?? []).length) {\n <p class=\"deb-hint\">When this flips to true, copy one field's value into another.</p>\n }\n }\n\n <mat-divider></mat-divider>\n\n <!-- Rules attached to this field -->\n <ngx-field-rules-list />\n\n <mat-divider></mat-divider>\n\n <!-- Cross-entity field reference & drift management (Phase 8) -->\n <ngx-referenced-field-config />\n </div>\n} @else {\n <div class=\"deb-inspector__empty\">\n <mat-icon>touch_app</mat-icon>\n <p>Select a field to edit its properties.</p>\n </div>\n}\n", styles: [".deb-inspector{display:flex;flex-direction:column;gap:12px}.deb-inspector__empty{color:var(--deb-muted, #6b7280);text-align:center;padding:24px 8px}.deb-full{width:100%}.deb-row{display:flex;gap:8px;align-items:center}.deb-row>*{flex:1}.deb-row--split{justify-content:space-between}.deb-row--split>*{flex:0 0 auto}.deb-toggles{display:grid;grid-template-columns:1fr 1fr;gap:8px 12px}.deb-option-row{display:flex;gap:6px;align-items:center}.deb-chip{display:inline-block;padding:2px 8px;border-radius:12px;background:var(--deb-accent-soft, #eef2ff);color:var(--deb-accent, #6366f1);font-size:12px;font-weight:600}\n"] }]
|
|
138
|
-
}] });
|
|
139
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmllbGQtaW5zcGVjdG9yLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9maWVsZC1pbnNwZWN0b3IuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vc3JjL2xpYi9jb21wb25lbnRzL2ZpZWxkLWluc3BlY3Rvci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDNUQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzdDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUMvRCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUM3RCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUNsRSxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDdkQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3pELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN0RSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUU3RCxPQUFPLEVBQUUsWUFBWSxFQUFFLGtCQUFrQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDeEUsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3hELE9BQU8sRUFBRSxnQkFBZ0IsRUFBc0IsTUFBTSxrQkFBa0IsQ0FBQztBQUN4RSxPQUFPLEVBQUUsOEJBQThCLEVBQUUs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QkFBOEI7cUJBQy9CIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBjb21wdXRlZCwgaW5qZWN0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBGb3Jtc01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcbmltcG9ydCB7IE1hdEJ1dHRvbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2J1dHRvbic7XG5pbXBvcnQgeyBNYXRDaGVja2JveE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2NoZWNrYm94JztcbmltcG9ydCB7IE1hdERpdmlkZXJNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9kaXZpZGVyJztcbmltcG9ydCB7IE1hdEZvcm1GaWVsZE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2Zvcm0tZmllbGQnO1xuaW1wb3J0IHsgTWF0SWNvbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2ljb24nO1xuaW1wb3J0IHsgTWF0SW5wdXRNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9pbnB1dCc7XG5pbXBvcnQgeyBNYXRTZWxlY3RNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9zZWxlY3QnO1xuaW1wb3J0IHsgTWF0U2xpZGVUb2dnbGVNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9zbGlkZS10b2dnbGUnO1xuaW1wb3J0IHsgTWF0VG9vbHRpcE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3Rvb2x0aXAnO1xuaW1wb3J0IHR5cGUgeyBEcm9wZG93bk9wdGlvbiwgTmVzdGVkRmllbGRDb25maWcgfSBmcm9tICdAZHluYW1pYy1lbnRpdHkvY29yZSc7XG5pbXBvcnQgeyByZXNvbHZlTGFiZWwsIHJlc29sdmVPcHRpb25MYWJlbCB9IGZyb20gJ0BkeW5hbWljLWVudGl0eS9jb3JlJztcbmltcG9ydCB7IEJ1aWxkZXJTdG9yZSB9IGZyb20gJy4uL2J1aWxkZXItc3RvcmUuc2VydmljZSc7XG5pbXBvcnQgeyBnZXRGaWVsZFR5cGVNZXRhLCB0eXBlIEZpZWxkVHlwZU1ldGEgfSBmcm9tICcuLi9maWVsZC1jYXRhbG9nJztcbmltcG9ydCB7IEVudGl0eVJlZmVyZW5jZUNvbmZpZ0NvbXBvbmVudCB9IGZyb20gJy4vZW50aXR5LXJlZmVyZW5jZS1jb25maWcuY29tcG9uZW50JztcbmltcG9ydCB7IEZpZWxkUnVsZXNMaXN0Q29tcG9uZW50IH0gZnJvbSAnLi9maWVsZC1ydWxlcy1saXN0LmNvbXBvbmVudCc7XG5pbXBvcnQgeyBSZWZlcmVuY2VkRmllbGRDb25maWdDb21wb25lbnQgfSBmcm9tICcuL3JlZmVyZW5jZWQtZmllbGQtY29uZmlnLmNvbXBvbmVudCc7XG5cbi8qKlxuICogRmllbGRJbnNwZWN0b3JDb21wb25lbnQg4oCUIGVkaXRzIGV2ZXJ5IHByb3BlcnR5IG9mIHRoZSBjdXJyZW50bHkgc2VsZWN0ZWQgZmllbGQuXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ25neC1maWVsZC1pbnNwZWN0b3InLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbXG4gICAgRm9ybXNNb2R1bGUsXG4gICAgTWF0QnV0dG9uTW9kdWxlLFxuICAgIE1hdENoZWNrYm94TW9kdWxlLFxuICAgIE1hdERpdmlkZXJNb2R1bGUsXG4gICAgTWF0Rm9ybUZpZWxkTW9kdWxlLFxuICAgIE1hdEljb25Nb2R1bGUsXG4gICAgTWF0SW5wdXRNb2R1bGUsXG4gICAgTWF0U2VsZWN0TW9kdWxlLFxuICAgIE1hdFNsaWRlVG9nZ2xlTW9kdWxlLFxuICAgIE1hdFRvb2x0aXBNb2R1bGUsXG4gICAgRW50aXR5UmVmZXJlbmNlQ29uZmlnQ29tcG9uZW50LFxuICAgIEZpZWxkUnVsZXNMaXN0Q29tcG9uZW50LFxuICAgIFJlZmVyZW5jZWRGaWVsZENvbmZpZ0NvbXBvbmVudCxcbiAgXSxcbiAgdGVtcGxhdGVVcmw6ICcuL2ZpZWxkLWluc3BlY3Rvci5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlczogW1xuICAgIGBcbiAgICAgIC5kZWItaW5zcGVjdG9yIHtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcbiAgICAgICAgZ2FwOiAxMnB4O1xuICAgICAgfVxuICAgICAgLmRlYi1pbnNwZWN0b3JfX2VtcHR5IHtcbiAgICAgICAgY29sb3I6IHZhcigtLWRlYi1tdXRlZCwgIzZiNzI4MCk7XG4gICAgICAgIHRleHQtYWxpZ246IGNlbnRlcjtcbiAgICAgICAgcGFkZGluZzogMjRweCA4cHg7XG4gICAgICB9XG4gICAgICAuZGViLWZ1bGwge1xuICAgICAgICB3aWR0aDogMTAwJTtcbiAgICAgIH1cbiAgICAgIC5kZWItcm93IHtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgZ2FwOiA4cHg7XG4gICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gICAgICB9XG4gICAgICAuZGViLXJvdyA+ICoge1xuICAgICAgICBmbGV4OiAxO1xuICAgICAgfVxuICAgICAgLmRlYi1yb3ctLXNwbGl0IHtcbiAgICAgICAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuO1xuICAgICAgfVxuICAgICAgLmRlYi1yb3ctLXNwbGl0ID4gKiB7XG4gICAgICAgIGZsZXg6IDAgMCBhdXRvO1xuICAgICAgfVxuICAgICAgLmRlYi10b2dnbGVzIHtcbiAgICAgICAgZGlzcGxheTogZ3JpZDtcbiAgICAgICAgZ3JpZC10ZW1wbGF0ZS1jb2x1bW5zOiAxZnIgMWZyO1xuICAgICAgICBnYXA6IDhweCAxMnB4O1xuICAgICAgfVxuICAgICAgLmRlYi1vcHRpb24tcm93IHtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgZ2FwOiA2cHg7XG4gICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gICAgICB9XG4gICAgICAuZGViLWNoaXAge1xuICAgICAgICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7XG4gICAgICAgIHBhZGRpbmc6IDJweCA4cHg7XG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDEycHg7XG4gICAgICAgIGJhY2tncm91bmQ6IHZhcigtLWRlYi1hY2NlbnQtc29mdCwgI2VlZjJmZik7XG4gICAgICAgIGNvbG9yOiB2YXIoLS1kZWItYWNjZW50LCAjNjM2NmYxKTtcbiAgICAgICAgZm9udC1zaXplOiAxMnB4O1xuICAgICAgICBmb250LXdlaWdodDogNjAwO1xuICAgICAgfVxuICAgIGAsXG4gIF0sXG59KVxuZXhwb3J0IGNsYXNzIEZpZWxkSW5zcGVjdG9yQ29tcG9uZW50IHtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHN0b3JlID0gaW5qZWN0KEJ1aWxkZXJTdG9yZSk7XG5cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGZpZWxkID0gdGhpcy5zdG9yZS5zZWxlY3RlZEZpZWxkO1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgbWV0YSA9IGNvbXB1dGVkPEZpZWxkVHlwZU1ldGEgfCB1bmRlZmluZWQ+KCgpID0+IHtcbiAgICBjb25zdCBmID0gdGhpcy5maWVsZCgpO1xuICAgIHJldHVybiBmID8gZ2V0RmllbGRUeXBlTWV0YShmLnR5cGUpIDogdW5kZWZpbmVkO1xuICB9KTtcblxuICBwcm90ZWN0ZWQgbGFuZygpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLnN0b3JlLmFjdGl2ZUxhbmd1YWdlKCk7XG4gIH1cblxuXG4gIHByb3RlY3RlZCB0b051bSh2YWx1ZTogdW5rbm93bik6IG51bWJlciB8IG51bGwge1xuICAgIGlmICh2YWx1ZSA9PT0gJycgfHwgdmFsdWUgPT09IG51bGwgfHwgdmFsdWUgPT09IHVuZGVmaW5lZCkgcmV0dXJuIG51bGw7XG4gICAgY29uc3QgbiA9IE51bWJlcih2YWx1ZSk7XG4gICAgcmV0dXJuIE51bWJlci5pc05hTihuKSA/IG51bGwgOiBuO1xuICB9XG5cbiAgcHJvdGVjdGVkIGxhYmVsVmFsdWUoZmllbGQ6IE5lc3RlZEZpZWxkQ29uZmlnKTogc3RyaW5nIHtcbiAgICByZXR1cm4gcmVzb2x2ZUxhYmVsKGZpZWxkLmxhYmVsLCB0aGlzLmxhbmcoKSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcGxhY2Vob2xkZXJWYWx1ZShmaWVsZDogTmVzdGVkRmllbGRDb25maWcpOiBzdHJpbmcge1xuICAgIHJldHVybiByZXNvbHZlTGFiZWwoZmllbGQucGxhY2Vob2xkZXIsIHRoaXMubGFuZygpKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBvcHRpb25MYWJlbChvcHRpb246IERyb3Bkb3duT3B0aW9uKTogc3RyaW5nIHtcbiAgICByZXR1cm4gcmVzb2x2ZU9wdGlvbkxhYmVsKG9wdGlvbiwgdGhpcy5sYW5nKCkpO1xuICB9XG5cbiAgLy8g4pSA4pSA4pSAIHNob3dXaGVuIGVkaXRpbmcg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgcHJvdGVjdGVkIHNob3dXaGVuRW50cmllcyhmaWVsZDogTmVzdGVkRmllbGRDb25maWcpOiB7IGtleTogc3RyaW5nOyBkaXNwbGF5OiBzdHJpbmcgfVtdIHtcbiAgICByZXR1cm4gT2JqZWN0LmVudHJpZXMoZmllbGQuc2hvd1doZW4gPz8ge30pLm1hcCgoW2tleSwgdmFsdWVdKSA9PiAoe1xuICAgICAga2V5LFxuICAgICAgZGlzcGxheTogdGhpcy5zdHJpbmdpZnlTaG93V2hlbih2YWx1ZSksXG4gICAgfSkpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFkZFNob3dXaGVuKGZpZWxkOiBOZXN0ZWRGaWVsZENvbmZpZyk6IHZvaWQge1xuICAgIGNvbnN0IG5leHQgPSB7IC4uLihmaWVsZC5zaG93V2hlbiA/PyB7fSkgfTtcbiAgICBsZXQga2V5ID0gJ2ZpZWxkJztcbiAgICBsZXQgbiA9IDE7XG4gICAgd2hpbGUgKGtleSBpbiBuZXh0KSBrZXkgPSBgZmllbGRfJHsrK259YDtcbiAgICBuZXh0W2tleV0gPSB0cnVlO1xuICAgIHRoaXMuc3RvcmUuc2V0U2hvd1doZW4oZmllbGQuaWQsIG5leHQpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHJlbmFtZVNob3dXaGVuKGZpZWxkOiBOZXN0ZWRGaWVsZENvbmZpZywgb2xkS2V5OiBzdHJpbmcsIG5ld0tleTogc3RyaW5nKTogdm9pZCB7XG4gICAgY29uc3QgdHJpbW1lZCA9IG5ld0tleS50cmltKCk7XG4gICAgaWYgKCF0cmltbWVkIHx8IHRyaW1tZWQgPT09IG9sZEtleSkgcmV0dXJuO1xuICAgIGNvbnN0IGN1cnJlbnQgPSBmaWVsZC5zaG93V2hlbiA/PyB7fTtcbiAgICBpZiAodHJpbW1lZCBpbiBjdXJyZW50KSByZXR1cm47XG4gICAgY29uc3QgbmV4dDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7fTtcbiAgICAvLyBSZWJ1aWxkIGluIG9yZGVyIHNvIHRoZSByb3cgZG9lcyBub3QganVtcCB3aGlsZSB0aGUgdXNlciBpcyB0eXBpbmcuXG4gICAgZm9yIChjb25zdCBbaywgdl0gb2YgT2JqZWN0LmVudHJpZXMoY3VycmVudCkpIG5leHRbayA9PT0gb2xkS2V5ID8gdHJpbW1lZCA6IGtdID0gdjtcbiAgICB0aGlzLnN0b3JlLnNldFNob3dXaGVuKGZpZWxkLmlkLCBuZXh0KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzZXRTaG93V2hlblZhbHVlKGZpZWxkOiBOZXN0ZWRGaWVsZENvbmZpZywga2V5OiBzdHJpbmcsIHJhdzogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5zdG9yZS5zZXRTaG93V2hlbihmaWVsZC5pZCwgeyAuLi4oZmllbGQuc2hvd1doZW4gPz8ge30pLCBba2V5XTogdGhpcy5wYXJzZVNob3dXaGVuKHJhdykgfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcmVtb3ZlU2hvd1doZW4oZmllbGQ6IE5lc3RlZEZpZWxkQ29uZmlnLCBrZXk6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IG5leHQgPSB7IC4uLihmaWVsZC5zaG93V2hlbiA/PyB7fSkgfTtcbiAgICBkZWxldGUgbmV4dFtrZXldO1xuICAgIHRoaXMuc3RvcmUuc2V0U2hvd1doZW4oZmllbGQuaWQsIG5leHQpO1xuICB9XG5cbiAgLyoqXG4gICAqIGBzaG93V2hlbmAgY29tcGFyZXMgd2l0aCBgIT09YCwgc28gdGhlIGF1dGhvcmVkIHR5cGUgaGFzIHRvIHN1cnZpdmUgdGhlIHJvdW5kIHRyaXA6XG4gICAqIFwidHJ1ZVwiL1wiZmFsc2VcIiBiZWNvbWUgYm9vbGVhbnMgYW5kIG51bWVyaWMgdGV4dCBiZWNvbWVzIGEgbnVtYmVyLlxuICAgKi9cbiAgcHJpdmF0ZSBwYXJzZVNob3dXaGVuKHJhdzogc3RyaW5nKTogdW5rbm93biB7XG4gICAgY29uc3QgdHJpbW1lZCA9IHJhdy50cmltKCk7XG4gICAgaWYgKHRyaW1tZWQgPT09ICd0cnVlJykgcmV0dXJuIHRydWU7XG4gICAgaWYgKHRyaW1tZWQgPT09ICdmYWxzZScpIHJldHVybiBmYWxzZTtcbiAgICBpZiAodHJpbW1lZCA9PT0gJ251bGwnKSByZXR1cm4gbnVsbDtcbiAgICBpZiAodHJpbW1lZCAhPT0gJycgJiYgIU51bWJlci5pc05hTihOdW1iZXIodHJpbW1lZCkpKSByZXR1cm4gTnVtYmVyKHRyaW1tZWQpO1xuICAgIHJldHVybiByYXc7XG4gIH1cblxuICBwcml2YXRlIHN0cmluZ2lmeVNob3dXaGVuKHZhbHVlOiB1bmtub3duKTogc3RyaW5nIHtcbiAgICBpZiAodmFsdWUgPT09IG51bGwpIHJldHVybiAnbnVsbCc7XG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcpIHJldHVybiBKU09OLnN0cmluZ2lmeSh2YWx1ZSk7XG4gICAgcmV0dXJuIFN0cmluZyh2YWx1ZSk7XG4gIH1cbn1cbiIsIkBpZiAoZmllbGQoKTsgYXMgZikge1xuICA8ZGl2IGNsYXNzPVwiZGViLWluc3BlY3RvclwiPlxuICAgIDwhLS0gSWRlbnRpdHkgLS0+XG4gICAgPGRpdiBjbGFzcz1cImRlYi1yb3dcIj5cbiAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIHN1YnNjcmlwdFNpemluZz1cImR5bmFtaWNcIj5cbiAgICAgICAgPG1hdC1sYWJlbD5GaWVsZCBpZDwvbWF0LWxhYmVsPlxuICAgICAgICA8aW5wdXQgbWF0SW5wdXQgZGF0YS10ZXN0aWQ9XCJmaWVsZC1pZFwiIFt2YWx1ZV09XCJmLmlkXCIgZGlzYWJsZWQgLz5cbiAgICAgICAgQGlmIChzdG9yZS5oYXNNYW51YWxJZChmLmlkKSkge1xuICAgICAgICAgIDxtYXQtaGludD5UaGUgc3RvcmVkIGRhdGEga2V5IGZvciB0aGlzIHNhdmVkIGZpZWxkIOKAlCBmaXhlZC48L21hdC1oaW50PlxuICAgICAgICB9IEBlbHNlIHtcbiAgICAgICAgICA8bWF0LWhpbnQ+RGVyaXZlZCBmcm9tIHRoZSBsYWJlbC4gVXNlZCBhcyB0aGUgZGF0YSBrZXkuPC9tYXQtaGludD5cbiAgICAgICAgfVxuICAgICAgPC9tYXQtZm9ybS1maWVsZD5cbiAgICAgIDxzcGFuIGNsYXNzPVwiZGViLWNoaXBcIj57eyBtZXRhKCk/LmxhYmVsIHx8IGYudHlwZSB9fTwvc3Bhbj5cbiAgICA8L2Rpdj5cblxuICAgIDwhLS0gTG9jYWxpc2VkIHRleHQgKGFjdGl2ZSBsYW5ndWFnZSkgLS0+XG4gICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCIgc3Vic2NyaXB0U2l6aW5nPVwiZHluYW1pY1wiIGNsYXNzPVwiZGViLWZ1bGxcIj5cbiAgICAgIDxtYXQtbGFiZWw+TGFiZWwgKHt7IGxhbmcoKSB9fSk8L21hdC1sYWJlbD5cbiAgICAgIDxpbnB1dFxuICAgICAgICBtYXRJbnB1dFxuICAgICAgICBbbmdNb2RlbF09XCJsYWJlbFZhbHVlKGYpXCJcbiAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwic3RvcmUuc2V0RmllbGRMYWJlbChmLmlkLCBsYW5nKCksICRldmVudClcIlxuICAgICAgLz5cbiAgICA8L21hdC1mb3JtLWZpZWxkPlxuXG4gICAgQGlmIChtZXRhKCk/LnN1cHBvcnRzUGxhY2Vob2xkZXIpIHtcbiAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIHN1YnNjcmlwdFNpemluZz1cImR5bmFtaWNcIiBjbGFzcz1cImRlYi1mdWxsXCI+XG4gICAgICAgIDxtYXQtbGFiZWw+UGxhY2Vob2xkZXIgKHt7IGxhbmcoKSB9fSk8L21hdC1sYWJlbD5cbiAgICAgICAgPGlucHV0XG4gICAgICAgICAgbWF0SW5wdXRcbiAgICAgICAgICBbbmdNb2RlbF09XCJwbGFjZWhvbGRlclZhbHVlKGYpXCJcbiAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzdG9yZS5zZXRGaWVsZFBsYWNlaG9sZGVyKGYuaWQsIGxhbmcoKSwgJGV2ZW50KVwiXG4gICAgICAgIC8+XG4gICAgICA8L21hdC1mb3JtLWZpZWxkPlxuICAgIH1cblxuICAgIEBpZiAobWV0YSgpPy5zdXBwb3J0c0RlZmF1bHRWYWx1ZSkge1xuICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCIgc3Vic2NyaXB0U2l6aW5nPVwiZHluYW1pY1wiIGNsYXNzPVwiZGViLWZ1bGxcIj5cbiAgICAgICAgPG1hdC1sYWJlbD5EZWZhdWx0IHZhbHVlPC9tYXQtbGFiZWw+XG4gICAgICAgIDxpbnB1dFxuICAgICAgICAgIG1hdElucHV0XG4gICAgICAgICAgW25nTW9kZWxdPVwiZi5kZWZhdWx0VmFsdWUgPz8gJydcIlxuICAgICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cInN0b3JlLnVwZGF0ZUZpZWxkKGYuaWQsIHsgZGVmYXVsdFZhbHVlOiAkZXZlbnQgfSlcIlxuICAgICAgICAvPlxuICAgICAgPC9tYXQtZm9ybS1maWVsZD5cbiAgICB9XG5cbiAgICA8IS0tIEVudGl0eSByZWZlcmVuY2UsIGNhc2NhZGUsIGFuZCBhdXRvUGF0Y2ggLS0+XG4gICAgQGlmIChtZXRhKCk/LmlzRW50aXR5UmVmKSB7XG4gICAgICA8bWF0LWRpdmlkZXI+PC9tYXQtZGl2aWRlcj5cbiAgICAgIDxuZ3gtZW50aXR5LXJlZmVyZW5jZS1jb25maWcgLz5cbiAgICB9XG5cbiAgICA8bWF0LWRpdmlkZXI+PC9tYXQtZGl2aWRlcj5cblxuICAgIDwhLS0gVmFsaWRhdG9ycyAtLT5cbiAgICBAaWYgKG1ldGEoKTsgYXMgbSkge1xuICAgICAgQGlmIChtLmZsYWdWYWxpZGF0b3JzLmxlbmd0aCB8fCBtLnBhcmFtVmFsaWRhdG9ycy5sZW5ndGgpIHtcbiAgICAgICAgPHNwYW4gY2xhc3M9XCJkZWItc2VjdGlvbi10aXRsZVwiPlZhbGlkYXRpb248L3NwYW4+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJkZWItdG9nZ2xlc1wiPlxuICAgICAgICAgIEBmb3IgKHYgb2YgbS5mbGFnVmFsaWRhdG9yczsgdHJhY2sgdikge1xuICAgICAgICAgICAgPG1hdC1jaGVja2JveFxuICAgICAgICAgICAgICBbbmdNb2RlbF09XCJzdG9yZS5oYXNGbGFnVmFsaWRhdG9yKGYsIHYpXCJcbiAgICAgICAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwic3RvcmUudG9nZ2xlRmxhZ1ZhbGlkYXRvcihmLmlkLCB2LCAkZXZlbnQpXCJcbiAgICAgICAgICAgID57eyB2IH19PC9tYXQtY2hlY2tib3g+XG4gICAgICAgICAgfVxuICAgICAgICA8L2Rpdj5cbiAgICAgICAgQGZvciAodiBvZiBtLnBhcmFtVmFsaWRhdG9yczsgdHJhY2sgdikge1xuICAgICAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIHN1YnNjcmlwdFNpemluZz1cImR5bmFtaWNcIiBjbGFzcz1cImRlYi1mdWxsXCI+XG4gICAgICAgICAgICA8bWF0LWxhYmVsPnt7IHYgfX08L21hdC1sYWJlbD5cbiAgICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgICBtYXRJbnB1dFxuICAgICAgICAgICAgICB0eXBlPVwibnVtYmVyXCJcbiAgICAgICAgICAgICAgW25nTW9kZWxdPVwic3RvcmUuZ2V0UGFyYW1WYWxpZGF0b3IoZiwgdilcIlxuICAgICAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzdG9yZS5zZXRQYXJhbVZhbGlkYXRvcihmLmlkLCB2LCB0b051bSgkZXZlbnQpKVwiXG4gICAgICAgICAgICAvPlxuICAgICAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICA8IS0tIE9wdGlvbnMgKGRyb3Bkb3duIC8gcmFkaW8gLyBtdWx0aVNlbGVjdCkgLS0+XG4gICAgQGlmIChtZXRhKCk/Lmhhc09wdGlvbnMpIHtcbiAgICAgIDxtYXQtZGl2aWRlcj48L21hdC1kaXZpZGVyPlxuICAgICAgPHNwYW4gY2xhc3M9XCJkZWItc2VjdGlvbi10aXRsZVwiPkRhdGEgc291cmNlPC9zcGFuPlxuICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCIgc3Vic2NyaXB0U2l6aW5nPVwiZHluYW1pY1wiIGNsYXNzPVwiZGViLWZ1bGxcIj5cbiAgICAgICAgPG1hdC1sYWJlbD5PcHRpb25zIGNvbWUgZnJvbTwvbWF0LWxhYmVsPlxuICAgICAgICA8bWF0LXNlbGVjdFxuICAgICAgICAgIGRhdGEtdGVzdGlkPVwiZGF0YS1zb3VyY2VcIlxuICAgICAgICAgIFtuZ01vZGVsXT1cInN0b3JlLmZpZWxkRGF0YVNvdXJjZShmKVwiXG4gICAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwic3RvcmUuc2V0RmllbGREYXRhU291cmNlKGYuaWQsICRldmVudClcIlxuICAgICAgICA+XG4gICAgICAgICAgPG1hdC1vcHRpb24gdmFsdWU9XCJub25lXCI+Tm90aGluZyB5ZXQ8L21hdC1vcHRpb24+XG4gICAgICAgICAgPG1hdC1vcHRpb24gdmFsdWU9XCJtYW51YWxcIj5PcHRpb25zIGF1dGhvcmVkIGhlcmU8L21hdC1vcHRpb24+XG4gICAgICAgICAgPG1hdC1vcHRpb24gdmFsdWU9XCJsb29rdXBcIj5BIG5hbWVkIGxpc3Q8L21hdC1vcHRpb24+XG4gICAgICAgIDwvbWF0LXNlbGVjdD5cbiAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XG4gICAgfVxuXG4gICAgPCEtLSBOYW1lZCBsb29rdXAgbGlzdCAtLT5cbiAgICBAaWYgKG1ldGEoKT8uaGFzT3B0aW9ucyAmJiBzdG9yZS5maWVsZERhdGFTb3VyY2UoZikgPT09ICdsb29rdXAnKSB7XG4gICAgICA8bWF0LWZvcm0tZmllbGQgYXBwZWFyYW5jZT1cIm91dGxpbmVcIiBzdWJzY3JpcHRTaXppbmc9XCJkeW5hbWljXCIgY2xhc3M9XCJkZWItZnVsbFwiPlxuICAgICAgICA8bWF0LWxhYmVsPkxpc3QgbmFtZTwvbWF0LWxhYmVsPlxuICAgICAgICA8aW5wdXRcbiAgICAgICAgICBtYXRJbnB1dFxuICAgICAgICAgIGRhdGEtdGVzdGlkPVwibGlzdC1uYW1lXCJcbiAgICAgICAgICBbbmdNb2RlbF09XCJmLmxpc3ROYW1lID8/ICcnXCJcbiAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzdG9yZS5zZXRMaXN0TmFtZShmLmlkLCAkZXZlbnQpXCJcbiAgICAgICAgLz5cbiAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XG4gICAgICA8cCBjbGFzcz1cImRlYi1oaW50XCI+XG4gICAgICAgIFRoZSBjb25zdW1pbmcgYXBwIHJlc29sdmVzIHRoaXMgbmFtZSB0aHJvdWdoIGl0cyBsb29rdXAgcmVnaXN0cnkuIE9wdGlvbnMgYXJlIG5vdFxuICAgICAgICBhdXRob3JlZCBoZXJlLlxuICAgICAgPC9wPlxuICAgIH1cblxuICAgIDwhLS0gSW5saW5lIG9wdGlvbnMgLS0+XG4gICAgQGlmIChtZXRhKCk/Lmhhc09wdGlvbnMgJiYgc3RvcmUuZmllbGREYXRhU291cmNlKGYpICE9PSAnbG9va3VwJykge1xuICAgICAgPGRpdiBjbGFzcz1cImRlYi1yb3dcIiBzdHlsZT1cImp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjtcIj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJkZWItc2VjdGlvbi10aXRsZVwiPk9wdGlvbnM8L3NwYW4+XG4gICAgICAgIDxidXR0b24gbWF0LXN0cm9rZWQtYnV0dG9uIHR5cGU9XCJidXR0b25cIiAoY2xpY2spPVwic3RvcmUuYWRkT3B0aW9uKGYuaWQpXCI+XG4gICAgICAgICAgPG1hdC1pY29uPmFkZDwvbWF0LWljb24+IE9wdGlvblxuICAgICAgICA8L2J1dHRvbj5cbiAgICAgIDwvZGl2PlxuICAgICAgQGZvciAob3B0IG9mIGYub3B0aW9ucyA/PyBbXTsgdHJhY2sgJGluZGV4KSB7XG4gICAgICAgIDxkaXYgY2xhc3M9XCJkZWItb3B0aW9uLXJvd1wiIGRhdGEtdGVzdGlkPVwib3B0aW9uLXJvd1wiPlxuICAgICAgICAgIDwhLS0gT25lIGlucHV0OiB0aGUgZGlzcGxheWVkIHRleHQgaXMgdGhlIHN0b3JlZCB2YWx1ZS4gLS0+XG4gICAgICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCIgc3Vic2NyaXB0U2l6aW5nPVwiZHluYW1pY1wiIGNsYXNzPVwiZGViLWZ1bGxcIj5cbiAgICAgICAgICAgIDxtYXQtbGFiZWw+T3B0aW9uICh7eyBsYW5nKCkgfX0pPC9tYXQtbGFiZWw+XG4gICAgICAgICAgICA8aW5wdXRcbiAgICAgICAgICAgICAgbWF0SW5wdXRcbiAgICAgICAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ29wdGlvbi0nICsgJGluZGV4XCJcbiAgICAgICAgICAgICAgW25nTW9kZWxdPVwib3B0aW9uTGFiZWwob3B0KVwiXG4gICAgICAgICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cInN0b3JlLnNldE9wdGlvbkxhYmVsKGYuaWQsICRpbmRleCwgbGFuZygpLCAkZXZlbnQpXCJcbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgPC9tYXQtZm9ybS1maWVsZD5cbiAgICAgICAgICA8YnV0dG9uIG1hdC1pY29uLWJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY29sb3I9XCJ3YXJuXCJcbiAgICAgICAgICAgIChjbGljayk9XCJzdG9yZS5yZW1vdmVPcHRpb24oZi5pZCwgJGluZGV4KVwiIG1hdFRvb2x0aXA9XCJSZW1vdmUgb3B0aW9uXCI+XG4gICAgICAgICAgICA8bWF0LWljb24+ZGVsZXRlPC9tYXQtaWNvbj5cbiAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgPC9kaXY+XG4gICAgICB9XG4gICAgICBAaWYgKCEoZi5vcHRpb25zID8/IFtdKS5sZW5ndGgpIHtcbiAgICAgICAgPHAgY2xhc3M9XCJkZWItaGludFwiPk5vIG9wdGlvbnMgeWV0IOKAlCBhZGQgYXQgbGVhc3Qgb25lLjwvcD5cbiAgICAgIH1cbiAgICB9XG5cbiAgICA8bWF0LWRpdmlkZXI+PC9tYXQtZGl2aWRlcj5cblxuICAgIDwhLS0gRmxhZ3MgLS0+XG4gICAgPHNwYW4gY2xhc3M9XCJkZWItc2VjdGlvbi10aXRsZVwiPkRpc3BsYXk8L3NwYW4+XG4gICAgPGRpdiBjbGFzcz1cImRlYi10b2dnbGVzXCI+XG4gICAgICA8bWF0LXNsaWRlLXRvZ2dsZVxuICAgICAgICBbbmdNb2RlbF09XCJmLnZpc2liaWxpdHkgPz8gdHJ1ZVwiXG4gICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cInN0b3JlLnVwZGF0ZUZpZWxkKGYuaWQsIHsgdmlzaWJpbGl0eTogJGV2ZW50IH0pXCJcbiAgICAgID5WaXNpYmxlPC9tYXQtc2xpZGUtdG9nZ2xlPlxuICAgICAgPG1hdC1zbGlkZS10b2dnbGVcbiAgICAgICAgW25nTW9kZWxdPVwiZi50YWJsZT8udmlzaWJsZSA/PyBmYWxzZVwiXG4gICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cInN0b3JlLnVwZGF0ZUZpZWxkKGYuaWQsIHsgdGFibGU6IHsgdmlzaWJsZTogJGV2ZW50IH0gfSlcIlxuICAgICAgPlRhYmxlIGNvbHVtbjwvbWF0LXNsaWRlLXRvZ2dsZT5cbiAgICAgIDxtYXQtc2xpZGUtdG9nZ2xlXG4gICAgICAgIFtuZ01vZGVsXT1cImYucmVhZG9ubHkgPz8gZmFsc2VcIlxuICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzdG9yZS51cGRhdGVGaWVsZChmLmlkLCB7IHJlYWRvbmx5OiAkZXZlbnQgfSlcIlxuICAgICAgPlJlYWQtb25seTwvbWF0LXNsaWRlLXRvZ2dsZT5cbiAgICAgIDxtYXQtc2xpZGUtdG9nZ2xlXG4gICAgICAgIFtuZ01vZGVsXT1cImYuZGlzYWJsZWQgPz8gZmFsc2VcIlxuICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzdG9yZS51cGRhdGVGaWVsZChmLmlkLCB7IGRpc2FibGVkOiAkZXZlbnQgfSlcIlxuICAgICAgPkRpc2FibGVkPC9tYXQtc2xpZGUtdG9nZ2xlPlxuICAgICAgPG1hdC1zbGlkZS10b2dnbGVcbiAgICAgICAgW25nTW9kZWxdPVwiZi5tYXNrRGF0YSA/PyBmYWxzZVwiXG4gICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cInN0b3JlLnVwZGF0ZUZpZWxkKGYuaWQsIHsgbWFza0RhdGE6ICRldmVudCB9KVwiXG4gICAgICA+TWFzayBkYXRhIChSQkFDKTwvbWF0LXNsaWRlLXRvZ2dsZT5cbiAgICAgIDxtYXQtc2xpZGUtdG9nZ2xlXG4gICAgICAgIGRhdGEtdGVzdGlkPVwidG9nZ2xlLWNyaXRpY2FsXCJcbiAgICAgICAgW25nTW9kZWxdPVwiZi5jcml0aWNhbEZpZWxkID8/IGZhbHNlXCJcbiAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwic3RvcmUudXBkYXRlRmllbGQoZi5pZCwgeyBjcml0aWNhbEZpZWxkOiAkZXZlbnQgfSlcIlxuICAgICAgICBtYXRUb29sdGlwPVwiUmVuZGVycyBsb2NrZWQ7IGVkaXRpbmcgaXQgYW5ub3VuY2VzIGEgY2hhbmdlIGFnYWluc3QgdGhlIHNlc3Npb24gYmFzZWxpbmUuXCJcbiAgICAgID5Dcml0aWNhbCBmaWVsZDwvbWF0LXNsaWRlLXRvZ2dsZT5cbiAgICAgIDxtYXQtc2xpZGUtdG9nZ2xlXG4gICAgICAgIFtuZ01vZGVsXT1cImYuc2hvd09uTWluaW1pemUgPz8gZmFsc2VcIlxuICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzdG9yZS51cGRhdGVGaWVsZChmLmlkLCB7IHNob3dPbk1pbmltaXplOiAkZXZlbnQgfSlcIlxuICAgICAgICBtYXRUb29sdGlwPVwiU2hvd24gaW4gdGhlIHJlY29yZCBlZGl0b3IncyBzdW1tYXJ5IHBhbmVsLlwiXG4gICAgICA+U2hvdyBpbiBzdW1tYXJ5PC9tYXQtc2xpZGUtdG9nZ2xlPlxuICAgIDwvZGl2PlxuXG4gICAgPG1hdC1kaXZpZGVyPjwvbWF0LWRpdmlkZXI+XG5cbiAgICA8IS0tIENvbmRpdGlvbmFsIHZpc2liaWxpdHkgKHN0YXRpYyBzaG93V2hlbikgLS0+XG4gICAgPGRpdiBjbGFzcz1cImRlYi1yb3cgZGViLXJvdy0tc3BsaXRcIj5cbiAgICAgIDxzcGFuIGNsYXNzPVwiZGViLXNlY3Rpb24tdGl0bGVcIj5TaG93IHdoZW48L3NwYW4+XG4gICAgICA8YnV0dG9uIG1hdC1zdHJva2VkLWJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgZGF0YS10ZXN0aWQ9XCJhZGQtc2hvdy13aGVuXCIgKGNsaWNrKT1cImFkZFNob3dXaGVuKGYpXCI+XG4gICAgICAgIDxtYXQtaWNvbj5hZGQ8L21hdC1pY29uPiBDb25kaXRpb25cbiAgICAgIDwvYnV0dG9uPlxuICAgIDwvZGl2PlxuICAgIEBmb3IgKGVudHJ5IG9mIHNob3dXaGVuRW50cmllcyhmKTsgdHJhY2sgZW50cnkua2V5KSB7XG4gICAgICA8ZGl2IGNsYXNzPVwiZGViLW9wdGlvbi1yb3dcIiBkYXRhLXRlc3RpZD1cIm9wdGlvbi1yb3dcIj5cbiAgICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCIgc3Vic2NyaXB0U2l6aW5nPVwiZHluYW1pY1wiPlxuICAgICAgICAgIDxtYXQtbGFiZWw+RmllbGQgaWQ8L21hdC1sYWJlbD5cbiAgICAgICAgICA8aW5wdXQgbWF0SW5wdXQgW25nTW9kZWxdPVwiZW50cnkua2V5XCIgKG5nTW9kZWxDaGFuZ2UpPVwicmVuYW1lU2hvd1doZW4oZiwgZW50cnkua2V5LCAkZXZlbnQpXCIgLz5cbiAgICAgICAgPC9tYXQtZm9ybS1maWVsZD5cbiAgICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCIgc3Vic2NyaXB0U2l6aW5nPVwiZHluYW1pY1wiPlxuICAgICAgICAgIDxtYXQtbGFiZWw+RXF1YWxzPC9tYXQtbGFiZWw+XG4gICAgICAgICAgPGlucHV0IG1hdElucHV0IFtuZ01vZGVsXT1cImVudHJ5LmRpc3BsYXlcIiAobmdNb2RlbENoYW5nZSk9XCJzZXRTaG93V2hlblZhbHVlKGYsIGVudHJ5LmtleSwgJGV2ZW50KVwiIC8+XG4gICAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XG4gICAgICAgIDxidXR0b24gbWF0LWljb24tYnV0dG9uIHR5cGU9XCJidXR0b25cIiBjb2xvcj1cIndhcm5cIiBtYXRUb29sdGlwPVwiUmVtb3ZlIGNvbmRpdGlvblwiXG4gICAgICAgICAgKGNsaWNrKT1cInJlbW92ZVNob3dXaGVuKGYsIGVudHJ5LmtleSlcIj5cbiAgICAgICAgICA8bWF0LWljb24+ZGVsZXRlPC9tYXQtaWNvbj5cbiAgICAgICAgPC9idXR0b24+XG4gICAgICA8L2Rpdj5cbiAgICB9XG4gICAgQGlmICghc2hvd1doZW5FbnRyaWVzKGYpLmxlbmd0aCkge1xuICAgICAgPHAgY2xhc3M9XCJkZWItaGludFwiPkFsd2F5cyB2aXNpYmxlLiBBZGQgYSBjb25kaXRpb24gdG8gc2hvdyB0aGlzIGZpZWxkIG9ubHkgZm9yIGNlcnRhaW4gdmFsdWVzLjwvcD5cbiAgICB9XG5cbiAgICA8IS0tIHBhdGNoT25UcnVlIChib29sZWFuL2NoZWNrYm94IG9ubHkpIC0tPlxuICAgIEBpZiAoZi50eXBlID09PSAnYm9vbGVhbicgfHwgZi50eXBlID09PSAnY2hlY2tib3gnKSB7XG4gICAgICA8bWF0LWRpdmlkZXI+PC9tYXQtZGl2aWRlcj5cbiAgICAgIDxkaXYgY2xhc3M9XCJkZWItcm93IGRlYi1yb3ctLXNwbGl0XCI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZGViLXNlY3Rpb24tdGl0bGVcIj5Db3B5IG9uIHRydWU8L3NwYW4+XG4gICAgICAgIDxidXR0b24gbWF0LXN0cm9rZWQtYnV0dG9uIHR5cGU9XCJidXR0b25cIiBkYXRhLXRlc3RpZD1cImFkZC1wYXRjaC1vbi10cnVlXCJcbiAgICAgICAgICAoY2xpY2spPVwic3RvcmUuYWRkUGF0Y2hPblRydWVNYXBwaW5nKGYuaWQpXCI+XG4gICAgICAgICAgPG1hdC1pY29uPmFkZDwvbWF0LWljb24+IE1hcHBpbmdcbiAgICAgICAgPC9idXR0b24+XG4gICAgICA8L2Rpdj5cbiAgICAgIEBmb3IgKG1hcHBpbmcgb2YgZi5wYXRjaE9uVHJ1ZSA/PyBbXTsgdHJhY2sgJGluZGV4KSB7XG4gICAgICAgIDxkaXYgY2xhc3M9XCJkZWItb3B0aW9uLXJvd1wiIGRhdGEtdGVzdGlkPVwib3B0aW9uLXJvd1wiPlxuICAgICAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIHN1YnNjcmlwdFNpemluZz1cImR5bmFtaWNcIj5cbiAgICAgICAgICAgIDxtYXQtbGFiZWw+RnJvbSBmaWVsZDwvbWF0LWxhYmVsPlxuICAgICAgICAgICAgPGlucHV0IG1hdElucHV0IFtuZ01vZGVsXT1cIm1hcHBpbmcuZnJvbVwiXG4gICAgICAgICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cInN0b3JlLnVwZGF0ZVBhdGNoT25UcnVlTWFwcGluZyhmLmlkLCAkaW5kZXgsIHsgZnJvbTogJGV2ZW50IH0pXCIgLz5cbiAgICAgICAgICA8L21hdC1mb3JtLWZpZWxkPlxuICAgICAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIHN1YnNjcmlwdFNpemluZz1cImR5bmFtaWNcIj5cbiAgICAgICAgICAgIDxtYXQtbGFiZWw+VG8gZmllbGQ8L21hdC1sYWJlbD5cbiAgICAgICAgICAgIDxpbnB1dCBtYXRJbnB1dCBbbmdNb2RlbF09XCJtYXBwaW5nLnRvXCJcbiAgICAgICAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwic3RvcmUudXBkYXRlUGF0Y2hPblRydWVNYXBwaW5nKGYuaWQsICRpbmRleCwgeyB0bzogJGV2ZW50IH0pXCIgLz5cbiAgICAgICAgICA8L21hdC1mb3JtLWZpZWxkPlxuICAgICAgICAgIDxidXR0b24gbWF0LWljb24tYnV0dG9uIHR5cGU9XCJidXR0b25cIiBjb2xvcj1cIndhcm5cIiBtYXRUb29sdGlwPVwiUmVtb3ZlIG1hcHBpbmdcIlxuICAgICAgICAgICAgKGNsaWNrKT1cInN0b3JlLnJlbW92ZVBhdGNoT25UcnVlTWFwcGluZyhmLmlkLCAkaW5kZXgpXCI+XG4gICAgICAgICAgICA8bWF0LWljb24+ZGVsZXRlPC9tYXQtaWNvbj5cbiAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgPC9kaXY+XG4gICAgICB9XG4gICAgICBAaWYgKCEoZi5wYXRjaE9uVHJ1ZSA/PyBbXSkubGVuZ3RoKSB7XG4gICAgICAgIDxwIGNsYXNzPVwiZGViLWhpbnRcIj5XaGVuIHRoaXMgZmxpcHMgdG8gdHJ1ZSwgY29weSBvbmUgZmllbGQncyB2YWx1ZSBpbnRvIGFub3RoZXIuPC9wPlxuICAgICAgfVxuICAgIH1cblxuICAgIDxtYXQtZGl2aWRlcj48L21hdC1kaXZpZGVyPlxuXG4gICAgPCEtLSBSdWxlcyBhdHRhY2hlZCB0byB0aGlzIGZpZWxkIC0tPlxuICAgIDxuZ3gtZmllbGQtcnVsZXMtbGlzdCAvPlxuXG4gICAgPG1hdC1kaXZpZGVyPjwvbWF0LWRpdmlkZXI+XG5cbiAgICA8IS0tIENyb3NzLWVudGl0eSBmaWVsZCByZWZlcmVuY2UgJiBkcmlmdCBtYW5hZ2VtZW50IChQaGFzZSA4KSAtLT5cbiAgICA8bmd4LXJlZmVyZW5jZWQtZmllbGQtY29uZmlnIC8+XG4gIDwvZGl2PlxufSBAZWxzZSB7XG4gIDxkaXYgY2xhc3M9XCJkZWItaW5zcGVjdG9yX19lbXB0eVwiPlxuICAgIDxtYXQtaWNvbj50b3VjaF9hcHA8L21hdC1pY29uPlxuICAgIDxwPlNlbGVjdCBhIGZpZWxkIHRvIGVkaXQgaXRzIHByb3BlcnRpZXMuPC9wPlxuICA8L2Rpdj5cbn1cbiJdfQ==
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { Component, EventEmitter, Output } from '@angular/core';
|
|
2
|
-
import { MatButtonModule } from '@angular/material/button';
|
|
3
|
-
import { MatIconModule } from '@angular/material/icon';
|
|
4
|
-
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
5
|
-
import { FIELD_TYPE_CATALOG } from '../field-catalog';
|
|
6
|
-
import * as i0 from "@angular/core";
|
|
7
|
-
import * as i1 from "@angular/material/button";
|
|
8
|
-
import * as i2 from "@angular/material/icon";
|
|
9
|
-
import * as i3 from "@angular/material/tooltip";
|
|
10
|
-
/**
|
|
11
|
-
* FieldPaletteComponent — the list of buildable field types (Angular Material).
|
|
12
|
-
*/
|
|
13
|
-
export class FieldPaletteComponent {
|
|
14
|
-
catalog = FIELD_TYPE_CATALOG;
|
|
15
|
-
pick = new EventEmitter();
|
|
16
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FieldPaletteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
17
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: FieldPaletteComponent, isStandalone: true, selector: "ngx-field-palette", outputs: { pick: "pick" }, ngImport: i0, template: `
|
|
18
|
-
<div class="deb-palette">
|
|
19
|
-
@for (meta of catalog; track meta.type) {
|
|
20
|
-
<button
|
|
21
|
-
mat-stroked-button
|
|
22
|
-
type="button"
|
|
23
|
-
class="deb-palette__item"
|
|
24
|
-
[attr.data-testid]="'palette-' + meta.type"
|
|
25
|
-
[matTooltip]="meta.description"
|
|
26
|
-
(click)="pick.emit(meta.type)"
|
|
27
|
-
>
|
|
28
|
-
<mat-icon>{{ meta.icon }}</mat-icon>
|
|
29
|
-
<span>{{ meta.label }}</span>
|
|
30
|
-
</button>
|
|
31
|
-
}
|
|
32
|
-
</div>
|
|
33
|
-
`, isInline: true, styles: [".deb-palette{display:grid;grid-template-columns:1fr 1fr;gap:8px}.deb-palette__item{justify-content:flex-start;overflow:hidden;border-radius:8px!important;border:1px solid var(--border, #e2e8f0)!important;padding:8px 12px!important;font-family:var(--font-heading, inherit)!important;font-weight:600!important;font-size:13px!important;transition:all .18s cubic-bezier(.16,1,.3,1)!important;background:#fff!important}.deb-palette__item:hover{border-color:#6366f1!important;background:#f5f3ff!important;color:#4f46e5!important;transform:translateY(-1px);box-shadow:0 4px 12px #6366f126!important}.deb-palette__item mat-icon{color:#6366f1;margin-right:6px;font-size:18px;width:18px;height:18px}.deb-palette__item span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
|
|
34
|
-
}
|
|
35
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FieldPaletteComponent, decorators: [{
|
|
36
|
-
type: Component,
|
|
37
|
-
args: [{ selector: 'ngx-field-palette', standalone: true, imports: [MatButtonModule, MatIconModule, MatTooltipModule], template: `
|
|
38
|
-
<div class="deb-palette">
|
|
39
|
-
@for (meta of catalog; track meta.type) {
|
|
40
|
-
<button
|
|
41
|
-
mat-stroked-button
|
|
42
|
-
type="button"
|
|
43
|
-
class="deb-palette__item"
|
|
44
|
-
[attr.data-testid]="'palette-' + meta.type"
|
|
45
|
-
[matTooltip]="meta.description"
|
|
46
|
-
(click)="pick.emit(meta.type)"
|
|
47
|
-
>
|
|
48
|
-
<mat-icon>{{ meta.icon }}</mat-icon>
|
|
49
|
-
<span>{{ meta.label }}</span>
|
|
50
|
-
</button>
|
|
51
|
-
}
|
|
52
|
-
</div>
|
|
53
|
-
`, styles: [".deb-palette{display:grid;grid-template-columns:1fr 1fr;gap:8px}.deb-palette__item{justify-content:flex-start;overflow:hidden;border-radius:8px!important;border:1px solid var(--border, #e2e8f0)!important;padding:8px 12px!important;font-family:var(--font-heading, inherit)!important;font-weight:600!important;font-size:13px!important;transition:all .18s cubic-bezier(.16,1,.3,1)!important;background:#fff!important}.deb-palette__item:hover{border-color:#6366f1!important;background:#f5f3ff!important;color:#4f46e5!important;transform:translateY(-1px);box-shadow:0 4px 12px #6366f126!important}.deb-palette__item mat-icon{color:#6366f1;margin-right:6px;font-size:18px;width:18px;height:18px}.deb-palette__item span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n"] }]
|
|
54
|
-
}], propDecorators: { pick: [{
|
|
55
|
-
type: Output
|
|
56
|
-
}] } });
|
|
57
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmllbGQtcGFsZXR0ZS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL2NvbXBvbmVudHMvZmllbGQtcGFsZXR0ZS5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ2hFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDdkQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFN0QsT0FBTyxFQUFFLGtCQUFrQixFQUFzQixNQUFNLGtCQUFrQixDQUFDOzs7OztBQUUxRTs7R0FFRztBQStESCxNQUFNLE9BQU8scUJBQXFCO0lBQ3ZCLE9BQU8sR0FBNkIsa0JBQWtCLENBQUM7SUFFdEQsSUFBSSxHQUFHLElBQUksWUFBWSxFQUFpQixDQUFDO3dHQUh4QyxxQkFBcUI7NEZBQXJCLHFCQUFxQix3R0ExRHRCOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JULDgwQkFqQlMsZUFBZSwyTkFBRSxhQUFhLG1MQUFFLGdCQUFnQjs7NEZBMkQvQyxxQkFBcUI7a0JBOURqQyxTQUFTOytCQUNFLG1CQUFtQixjQUNqQixJQUFJLFdBQ1AsQ0FBQyxlQUFlLEVBQUUsYUFBYSxFQUFFLGdCQUFnQixDQUFDLFlBQ2pEOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JUOzhCQTZDUyxJQUFJO3NCQUFiLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIEV2ZW50RW1pdHRlciwgT3V0cHV0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBNYXRCdXR0b25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9idXR0b24nO1xuaW1wb3J0IHsgTWF0SWNvbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2ljb24nO1xuaW1wb3J0IHsgTWF0VG9vbHRpcE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3Rvb2x0aXAnO1xuaW1wb3J0IHR5cGUgeyBSaWNoRmllbGRUeXBlIH0gZnJvbSAnQGR5bmFtaWMtZW50aXR5L2NvcmUnO1xuaW1wb3J0IHsgRklFTERfVFlQRV9DQVRBTE9HLCB0eXBlIEZpZWxkVHlwZU1ldGEgfSBmcm9tICcuLi9maWVsZC1jYXRhbG9nJztcblxuLyoqXG4gKiBGaWVsZFBhbGV0dGVDb21wb25lbnQg4oCUIHRoZSBsaXN0IG9mIGJ1aWxkYWJsZSBmaWVsZCB0eXBlcyAoQW5ndWxhciBNYXRlcmlhbCkuXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ25neC1maWVsZC1wYWxldHRlJyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW01hdEJ1dHRvbk1vZHVsZSwgTWF0SWNvbk1vZHVsZSwgTWF0VG9vbHRpcE1vZHVsZV0sXG4gIHRlbXBsYXRlOiBgXG4gICAgPGRpdiBjbGFzcz1cImRlYi1wYWxldHRlXCI+XG4gICAgICBAZm9yIChtZXRhIG9mIGNhdGFsb2c7IHRyYWNrIG1ldGEudHlwZSkge1xuICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgbWF0LXN0cm9rZWQtYnV0dG9uXG4gICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgY2xhc3M9XCJkZWItcGFsZXR0ZV9faXRlbVwiXG4gICAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3BhbGV0dGUtJyArIG1ldGEudHlwZVwiXG4gICAgICAgICAgW21hdFRvb2x0aXBdPVwibWV0YS5kZXNjcmlwdGlvblwiXG4gICAgICAgICAgKGNsaWNrKT1cInBpY2suZW1pdChtZXRhLnR5cGUpXCJcbiAgICAgICAgPlxuICAgICAgICAgIDxtYXQtaWNvbj57eyBtZXRhLmljb24gfX08L21hdC1pY29uPlxuICAgICAgICAgIDxzcGFuPnt7IG1ldGEubGFiZWwgfX08L3NwYW4+XG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgfVxuICAgIDwvZGl2PlxuICBgLFxuICBzdHlsZXM6IFtcbiAgICBgXG4gICAgICAuZGViLXBhbGV0dGUge1xuICAgICAgICBkaXNwbGF5OiBncmlkO1xuICAgICAgICBncmlkLXRlbXBsYXRlLWNvbHVtbnM6IDFmciAxZnI7XG4gICAgICAgIGdhcDogOHB4O1xuICAgICAgfVxuICAgICAgLmRlYi1wYWxldHRlX19pdGVtIHtcbiAgICAgICAganVzdGlmeS1jb250ZW50OiBmbGV4LXN0YXJ0O1xuICAgICAgICBvdmVyZmxvdzogaGlkZGVuO1xuICAgICAgICBib3JkZXItcmFkaXVzOiA4cHggIWltcG9ydGFudDtcbiAgICAgICAgYm9yZGVyOiAxcHggc29saWQgdmFyKC0tYm9yZGVyLCAjZTJlOGYwKSAhaW1wb3J0YW50O1xuICAgICAgICBwYWRkaW5nOiA4cHggMTJweCAhaW1wb3J0YW50O1xuICAgICAgICBmb250LWZhbWlseTogdmFyKC0tZm9udC1oZWFkaW5nLCBpbmhlcml0KSAhaW1wb3J0YW50O1xuICAgICAgICBmb250LXdlaWdodDogNjAwICFpbXBvcnRhbnQ7XG4gICAgICAgIGZvbnQtc2l6ZTogMTNweCAhaW1wb3J0YW50O1xuICAgICAgICB0cmFuc2l0aW9uOiBhbGwgMC4xOHMgY3ViaWMtYmV6aWVyKDAuMTYsIDEsIDAuMywgMSkgIWltcG9ydGFudDtcbiAgICAgICAgYmFja2dyb3VuZDogI2ZmZmZmZiAhaW1wb3J0YW50O1xuICAgICAgfVxuICAgICAgLmRlYi1wYWxldHRlX19pdGVtOmhvdmVyIHtcbiAgICAgICAgYm9yZGVyLWNvbG9yOiAjNjM2NmYxICFpbXBvcnRhbnQ7XG4gICAgICAgIGJhY2tncm91bmQ6ICNmNWYzZmYgIWltcG9ydGFudDtcbiAgICAgICAgY29sb3I6ICM0ZjQ2ZTUgIWltcG9ydGFudDtcbiAgICAgICAgdHJhbnNmb3JtOiB0cmFuc2xhdGVZKC0xcHgpO1xuICAgICAgICBib3gtc2hhZG93OiAwIDRweCAxMnB4IHJnYmEoOTksIDEwMiwgMjQxLCAwLjE1KSAhaW1wb3J0YW50O1xuICAgICAgfVxuICAgICAgLmRlYi1wYWxldHRlX19pdGVtIG1hdC1pY29uIHtcbiAgICAgICAgY29sb3I6ICM2MzY2ZjE7XG4gICAgICAgIG1hcmdpbi1yaWdodDogNnB4O1xuICAgICAgICBmb250LXNpemU6IDE4cHg7XG4gICAgICAgIHdpZHRoOiAxOHB4O1xuICAgICAgICBoZWlnaHQ6IDE4cHg7XG4gICAgICB9XG4gICAgICAuZGViLXBhbGV0dGVfX2l0ZW0gc3BhbiB7XG4gICAgICAgIG92ZXJmbG93OiBoaWRkZW47XG4gICAgICAgIHRleHQtb3ZlcmZsb3c6IGVsbGlwc2lzO1xuICAgICAgICB3aGl0ZS1zcGFjZTogbm93cmFwO1xuICAgICAgfVxuICAgIGAsXG4gIF0sXG59KVxuZXhwb3J0IGNsYXNzIEZpZWxkUGFsZXR0ZUNvbXBvbmVudCB7XG4gIHJlYWRvbmx5IGNhdGFsb2c6IHJlYWRvbmx5IEZpZWxkVHlwZU1ldGFbXSA9IEZJRUxEX1RZUEVfQ0FUQUxPRztcblxuICBAT3V0cHV0KCkgcGljayA9IG5ldyBFdmVudEVtaXR0ZXI8UmljaEZpZWxkVHlwZT4oKTtcbn1cbiJdfQ==
|
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import { Component, inject, signal } from '@angular/core';
|
|
2
|
-
import { MatButtonModule } from '@angular/material/button';
|
|
3
|
-
import { MatIconModule } from '@angular/material/icon';
|
|
4
|
-
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|
5
|
-
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
6
|
-
import { BuilderStore } from '../builder-store.service';
|
|
7
|
-
import { deepClone } from '../clone';
|
|
8
|
-
import { RuleFormComponent } from './rule-form.component';
|
|
9
|
-
import * as i0 from "@angular/core";
|
|
10
|
-
import * as i1 from "@angular/material/button";
|
|
11
|
-
import * as i2 from "@angular/material/icon";
|
|
12
|
-
import * as i3 from "@angular/material/slide-toggle";
|
|
13
|
-
import * as i4 from "@angular/material/tooltip";
|
|
14
|
-
/**
|
|
15
|
-
* FieldRulesListComponent — the rules attached to the selected field: view, reorder,
|
|
16
|
-
* enable/disable, edit, delete. `RuleFormComponent` authors one rule; this owns the set.
|
|
17
|
-
*/
|
|
18
|
-
export class FieldRulesListComponent {
|
|
19
|
-
store = inject(BuilderStore);
|
|
20
|
-
/** The rule currently open in the editor — a new draft or a copy of an existing rule. */
|
|
21
|
-
editing = signal(null);
|
|
22
|
-
startCreate() {
|
|
23
|
-
const fieldId = this.store.selectedFieldId();
|
|
24
|
-
if (!fieldId)
|
|
25
|
-
return;
|
|
26
|
-
this.editing.set({
|
|
27
|
-
formConfigId: this.store.config().entity || 'form-1',
|
|
28
|
-
fieldId,
|
|
29
|
-
conditions: [{ operator: 'EQUAL', compareType: 'value', value: '' }],
|
|
30
|
-
action: { type: 'visibility', value: false },
|
|
31
|
-
targets: [{ id: fieldId, type: 'field' }],
|
|
32
|
-
enabled: true,
|
|
33
|
-
priority: this.store.rules().length + 1,
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
startEdit(rule) {
|
|
37
|
-
this.editing.set(deepClone(rule));
|
|
38
|
-
}
|
|
39
|
-
commit(rule) {
|
|
40
|
-
if (rule.id)
|
|
41
|
-
this.store.updateRule(rule.id, rule);
|
|
42
|
-
else
|
|
43
|
-
this.store.addRule(rule);
|
|
44
|
-
this.editing.set(null);
|
|
45
|
-
}
|
|
46
|
-
/** One-line human summary: `status EQUAL "archived" → hide`. */
|
|
47
|
-
summarize(rule) {
|
|
48
|
-
const cond = rule.conditions
|
|
49
|
-
.map(c => {
|
|
50
|
-
const rhs = c.compareType === 'field' ? c.compareToField : JSON.stringify(c.value ?? '');
|
|
51
|
-
return `${c.operator} ${rhs ?? ''}`.trim();
|
|
52
|
-
})
|
|
53
|
-
.join(' AND ');
|
|
54
|
-
const action = rule.action.type === 'visibility'
|
|
55
|
-
? rule.action.value === false || rule.action.value === 'false'
|
|
56
|
-
? 'hide'
|
|
57
|
-
: 'show'
|
|
58
|
-
: `${rule.action.type}: ${rule.action.value}`;
|
|
59
|
-
return `${rule.fieldId} ${cond} → ${action}`;
|
|
60
|
-
}
|
|
61
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FieldRulesListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
62
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: FieldRulesListComponent, isStandalone: true, selector: "ngx-field-rules-list", ngImport: i0, template: `
|
|
63
|
-
<div class="deb-rules">
|
|
64
|
-
<div class="deb-rules__head">
|
|
65
|
-
<span class="deb-section-title">Rules</span>
|
|
66
|
-
<button
|
|
67
|
-
mat-stroked-button
|
|
68
|
-
type="button"
|
|
69
|
-
data-testid="add-rule"
|
|
70
|
-
[disabled]="!store.selectedFieldId()"
|
|
71
|
-
(click)="startCreate()"
|
|
72
|
-
>
|
|
73
|
-
<mat-icon>add</mat-icon> Rule
|
|
74
|
-
</button>
|
|
75
|
-
</div>
|
|
76
|
-
|
|
77
|
-
@if (editing(); as draft) {
|
|
78
|
-
<ngx-rule-form [rule]="draft" (save)="commit($event)" (cancel)="editing.set(null)" />
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
@if (store.rulesForSelectedField().length === 0 && !editing()) {
|
|
82
|
-
<p class="deb-hint" data-testid="rules-empty">No rules on this field.</p>
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
@for (rule of store.rulesForSelectedField(); track rule.id) {
|
|
86
|
-
<div class="deb-rule-item" [class.deb-rule-item--off]="!rule.enabled" data-testid="rule-item">
|
|
87
|
-
<div class="deb-rule-item__body">
|
|
88
|
-
<span class="deb-rule-item__summary">{{ summarize(rule) }}</span>
|
|
89
|
-
<span class="deb-rule-item__meta">
|
|
90
|
-
priority {{ rule.priority }} · {{ rule.targets.length }} target(s)
|
|
91
|
-
</span>
|
|
92
|
-
</div>
|
|
93
|
-
<div class="deb-rule-item__actions">
|
|
94
|
-
<mat-slide-toggle
|
|
95
|
-
[checked]="rule.enabled"
|
|
96
|
-
matTooltip="Enable / disable"
|
|
97
|
-
(change)="store.toggleRule(rule.id!, $event.checked)"
|
|
98
|
-
/>
|
|
99
|
-
<button
|
|
100
|
-
mat-icon-button
|
|
101
|
-
type="button"
|
|
102
|
-
matTooltip="Move up" aria-label="Move rule up"
|
|
103
|
-
[attr.data-testid]="'rule-up-' + rule.id"
|
|
104
|
-
(click)="store.moveRule(rule.id!, -1)"
|
|
105
|
-
>
|
|
106
|
-
<mat-icon>arrow_upward</mat-icon>
|
|
107
|
-
</button>
|
|
108
|
-
<button
|
|
109
|
-
mat-icon-button
|
|
110
|
-
type="button"
|
|
111
|
-
matTooltip="Move down" aria-label="Move rule down"
|
|
112
|
-
[attr.data-testid]="'rule-down-' + rule.id"
|
|
113
|
-
(click)="store.moveRule(rule.id!, 1)"
|
|
114
|
-
>
|
|
115
|
-
<mat-icon>arrow_downward</mat-icon>
|
|
116
|
-
</button>
|
|
117
|
-
<button
|
|
118
|
-
mat-icon-button
|
|
119
|
-
type="button"
|
|
120
|
-
matTooltip="Edit rule"
|
|
121
|
-
[attr.data-testid]="'rule-edit-' + rule.id"
|
|
122
|
-
(click)="startEdit(rule)"
|
|
123
|
-
>
|
|
124
|
-
<mat-icon>edit</mat-icon>
|
|
125
|
-
</button>
|
|
126
|
-
<button
|
|
127
|
-
mat-icon-button
|
|
128
|
-
type="button"
|
|
129
|
-
color="warn"
|
|
130
|
-
matTooltip="Delete rule"
|
|
131
|
-
[attr.data-testid]="'rule-delete-' + rule.id"
|
|
132
|
-
(click)="store.removeRule(rule.id!)"
|
|
133
|
-
>
|
|
134
|
-
<mat-icon>delete</mat-icon>
|
|
135
|
-
</button>
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
}
|
|
139
|
-
</div>
|
|
140
|
-
`, isInline: true, styles: [".deb-rules{display:flex;flex-direction:column;gap:8px}.deb-rules__head{display:flex;align-items:center;justify-content:space-between}.deb-rule-item{display:flex;align-items:center;justify-content:space-between;gap:8px;border:1px solid #e5e7eb;border-radius:8px;padding:8px 10px}.deb-rule-item--off{opacity:.55}.deb-rule-item__body{display:flex;flex-direction:column;min-width:0}.deb-rule-item__summary{font-size:13px;font-weight:600;overflow-wrap:anywhere}.deb-rule-item__meta{font-size:11px;color:#6b7280}.deb-rule-item__actions{display:flex;align-items:center;flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i3.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i4.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: RuleFormComponent, selector: "ngx-rule-form", inputs: ["rule"], outputs: ["save", "cancel"] }] });
|
|
141
|
-
}
|
|
142
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FieldRulesListComponent, decorators: [{
|
|
143
|
-
type: Component,
|
|
144
|
-
args: [{ selector: 'ngx-field-rules-list', standalone: true, imports: [MatButtonModule, MatIconModule, MatSlideToggleModule, MatTooltipModule, RuleFormComponent], template: `
|
|
145
|
-
<div class="deb-rules">
|
|
146
|
-
<div class="deb-rules__head">
|
|
147
|
-
<span class="deb-section-title">Rules</span>
|
|
148
|
-
<button
|
|
149
|
-
mat-stroked-button
|
|
150
|
-
type="button"
|
|
151
|
-
data-testid="add-rule"
|
|
152
|
-
[disabled]="!store.selectedFieldId()"
|
|
153
|
-
(click)="startCreate()"
|
|
154
|
-
>
|
|
155
|
-
<mat-icon>add</mat-icon> Rule
|
|
156
|
-
</button>
|
|
157
|
-
</div>
|
|
158
|
-
|
|
159
|
-
@if (editing(); as draft) {
|
|
160
|
-
<ngx-rule-form [rule]="draft" (save)="commit($event)" (cancel)="editing.set(null)" />
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
@if (store.rulesForSelectedField().length === 0 && !editing()) {
|
|
164
|
-
<p class="deb-hint" data-testid="rules-empty">No rules on this field.</p>
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
@for (rule of store.rulesForSelectedField(); track rule.id) {
|
|
168
|
-
<div class="deb-rule-item" [class.deb-rule-item--off]="!rule.enabled" data-testid="rule-item">
|
|
169
|
-
<div class="deb-rule-item__body">
|
|
170
|
-
<span class="deb-rule-item__summary">{{ summarize(rule) }}</span>
|
|
171
|
-
<span class="deb-rule-item__meta">
|
|
172
|
-
priority {{ rule.priority }} · {{ rule.targets.length }} target(s)
|
|
173
|
-
</span>
|
|
174
|
-
</div>
|
|
175
|
-
<div class="deb-rule-item__actions">
|
|
176
|
-
<mat-slide-toggle
|
|
177
|
-
[checked]="rule.enabled"
|
|
178
|
-
matTooltip="Enable / disable"
|
|
179
|
-
(change)="store.toggleRule(rule.id!, $event.checked)"
|
|
180
|
-
/>
|
|
181
|
-
<button
|
|
182
|
-
mat-icon-button
|
|
183
|
-
type="button"
|
|
184
|
-
matTooltip="Move up" aria-label="Move rule up"
|
|
185
|
-
[attr.data-testid]="'rule-up-' + rule.id"
|
|
186
|
-
(click)="store.moveRule(rule.id!, -1)"
|
|
187
|
-
>
|
|
188
|
-
<mat-icon>arrow_upward</mat-icon>
|
|
189
|
-
</button>
|
|
190
|
-
<button
|
|
191
|
-
mat-icon-button
|
|
192
|
-
type="button"
|
|
193
|
-
matTooltip="Move down" aria-label="Move rule down"
|
|
194
|
-
[attr.data-testid]="'rule-down-' + rule.id"
|
|
195
|
-
(click)="store.moveRule(rule.id!, 1)"
|
|
196
|
-
>
|
|
197
|
-
<mat-icon>arrow_downward</mat-icon>
|
|
198
|
-
</button>
|
|
199
|
-
<button
|
|
200
|
-
mat-icon-button
|
|
201
|
-
type="button"
|
|
202
|
-
matTooltip="Edit rule"
|
|
203
|
-
[attr.data-testid]="'rule-edit-' + rule.id"
|
|
204
|
-
(click)="startEdit(rule)"
|
|
205
|
-
>
|
|
206
|
-
<mat-icon>edit</mat-icon>
|
|
207
|
-
</button>
|
|
208
|
-
<button
|
|
209
|
-
mat-icon-button
|
|
210
|
-
type="button"
|
|
211
|
-
color="warn"
|
|
212
|
-
matTooltip="Delete rule"
|
|
213
|
-
[attr.data-testid]="'rule-delete-' + rule.id"
|
|
214
|
-
(click)="store.removeRule(rule.id!)"
|
|
215
|
-
>
|
|
216
|
-
<mat-icon>delete</mat-icon>
|
|
217
|
-
</button>
|
|
218
|
-
</div>
|
|
219
|
-
</div>
|
|
220
|
-
}
|
|
221
|
-
</div>
|
|
222
|
-
`, styles: [".deb-rules{display:flex;flex-direction:column;gap:8px}.deb-rules__head{display:flex;align-items:center;justify-content:space-between}.deb-rule-item{display:flex;align-items:center;justify-content:space-between;gap:8px;border:1px solid #e5e7eb;border-radius:8px;padding:8px 10px}.deb-rule-item--off{opacity:.55}.deb-rule-item__body{display:flex;flex-direction:column;min-width:0}.deb-rule-item__summary{font-size:13px;font-weight:600;overflow-wrap:anywhere}.deb-rule-item__meta{font-size:11px;color:#6b7280}.deb-rule-item__actions{display:flex;align-items:center;flex:0 0 auto}\n"] }]
|
|
223
|
-
}] });
|
|
224
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmllbGQtcnVsZXMtbGlzdC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL2NvbXBvbmVudHMvZmllbGQtcnVsZXMtbGlzdC5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzFELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDdkQsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDdEUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFN0QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3hELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDckMsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sdUJBQXVCLENBQUM7Ozs7OztBQUUxRDs7O0dBR0c7QUFrSUgsTUFBTSxPQUFPLHVCQUF1QjtJQUNmLEtBQUssR0FBRyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUM7SUFFaEQseUZBQXlGO0lBQ3RFLE9BQU8sR0FBRyxNQUFNLENBQWtCLElBQUksQ0FBQyxDQUFDO0lBRWpELFdBQVc7UUFDbkIsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxlQUFlLEVBQUUsQ0FBQztRQUM3QyxJQUFJLENBQUMsT0FBTztZQUFFLE9BQU87UUFDckIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUM7WUFDZixZQUFZLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxNQUFNLElBQUksUUFBUTtZQUNwRCxPQUFPO1lBQ1AsVUFBVSxFQUFFLENBQUMsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLFdBQVcsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEVBQUUsRUFBRSxDQUFDO1lBQ3BFLE1BQU0sRUFBRSxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRTtZQUM1QyxPQUFPLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxDQUFDO1lBQ3pDLE9BQU8sRUFBRSxJQUFJO1lBQ2IsUUFBUSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUMsTUFBTSxHQUFHLENBQUM7U0FDeEMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVTLFNBQVMsQ0FBQyxJQUFjO1FBQ2hDLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBQ3BDLENBQUM7SUFFUyxNQUFNLENBQUMsSUFBYztRQUM3QixJQUFJLElBQUksQ0FBQyxFQUFFO1lBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLEVBQUUsRUFBRSxJQUFJLENBQUMsQ0FBQzs7WUFDN0MsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDOUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDekIsQ0FBQztJQUVELGdFQUFnRTtJQUN0RCxTQUFTLENBQUMsSUFBYztRQUNoQyxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsVUFBVTthQUN6QixHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUU7WUFDUCxNQUFNLEdBQUcsR0FBRyxDQUFDLENBQUMsV0FBVyxLQUFLLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQyxDQUFDO1lBQ3pGLE9BQU8sR0FBRyxDQUFDLENBQUMsUUFBUSxJQUFJLEdBQUcsSUFBSSxFQUFFLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUM3QyxDQUFDLENBQUM7YUFDRCxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFakIsTUFBTSxNQUFNLEdBQ1YsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssWUFBWTtZQUMvQixDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEtBQUssS0FBSyxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxLQUFLLE9BQU87Z0JBQzVELENBQUMsQ0FBQyxNQUFNO2dCQUNSLENBQUMsQ0FBQyxNQUFNO1lBQ1YsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUVsRCxPQUFPLEdBQUcsSUFBSSxDQUFDLE9BQU8sSUFBSSxJQUFJLE1BQU0sTUFBTSxFQUFFLENBQUM7SUFDL0MsQ0FBQzt3R0EvQ1UsdUJBQXVCOzRGQUF2Qix1QkFBdUIsZ0ZBN0h4Qjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBOEVULDJvQkEvRVMsZUFBZSx3VUFBRSxhQUFhLG1MQUFFLG9CQUFvQixrV0FBRSxnQkFBZ0IsNlRBQUUsaUJBQWlCOzs0RkE4SHhGLHVCQUF1QjtrQkFqSW5DLFNBQVM7K0JBQ0Usc0JBQXNCLGNBQ3BCLElBQUksV0FDUCxDQUFDLGVBQWUsRUFBRSxhQUFhLEVBQUUsb0JBQW9CLEVBQUUsZ0JBQWdCLEVBQUUsaUJBQWlCLENBQUMsWUFDMUY7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQThFVCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgaW5qZWN0LCBzaWduYWwgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IE1hdEJ1dHRvbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2J1dHRvbic7XG5pbXBvcnQgeyBNYXRJY29uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvaWNvbic7XG5pbXBvcnQgeyBNYXRTbGlkZVRvZ2dsZU1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3NsaWRlLXRvZ2dsZSc7XG5pbXBvcnQgeyBNYXRUb29sdGlwTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvdG9vbHRpcCc7XG5pbXBvcnQgdHlwZSB7IEZvcm1SdWxlIH0gZnJvbSAnQGR5bmFtaWMtZW50aXR5L2NvcmUnO1xuaW1wb3J0IHsgQnVpbGRlclN0b3JlIH0gZnJvbSAnLi4vYnVpbGRlci1zdG9yZS5zZXJ2aWNlJztcbmltcG9ydCB7IGRlZXBDbG9uZSB9IGZyb20gJy4uL2Nsb25lJztcbmltcG9ydCB7IFJ1bGVGb3JtQ29tcG9uZW50IH0gZnJvbSAnLi9ydWxlLWZvcm0uY29tcG9uZW50JztcblxuLyoqXG4gKiBGaWVsZFJ1bGVzTGlzdENvbXBvbmVudCDigJQgdGhlIHJ1bGVzIGF0dGFjaGVkIHRvIHRoZSBzZWxlY3RlZCBmaWVsZDogdmlldywgcmVvcmRlcixcbiAqIGVuYWJsZS9kaXNhYmxlLCBlZGl0LCBkZWxldGUuIGBSdWxlRm9ybUNvbXBvbmVudGAgYXV0aG9ycyBvbmUgcnVsZTsgdGhpcyBvd25zIHRoZSBzZXQuXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ25neC1maWVsZC1ydWxlcy1saXN0JyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW01hdEJ1dHRvbk1vZHVsZSwgTWF0SWNvbk1vZHVsZSwgTWF0U2xpZGVUb2dnbGVNb2R1bGUsIE1hdFRvb2x0aXBNb2R1bGUsIFJ1bGVGb3JtQ29tcG9uZW50XSxcbiAgdGVtcGxhdGU6IGBcbiAgICA8ZGl2IGNsYXNzPVwiZGViLXJ1bGVzXCI+XG4gICAgICA8ZGl2IGNsYXNzPVwiZGViLXJ1bGVzX19oZWFkXCI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZGViLXNlY3Rpb24tdGl0bGVcIj5SdWxlczwvc3Bhbj5cbiAgICAgICAgPGJ1dHRvblxuICAgICAgICAgIG1hdC1zdHJva2VkLWJ1dHRvblxuICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgIGRhdGEtdGVzdGlkPVwiYWRkLXJ1bGVcIlxuICAgICAgICAgIFtkaXNhYmxlZF09XCIhc3RvcmUuc2VsZWN0ZWRGaWVsZElkKClcIlxuICAgICAgICAgIChjbGljayk9XCJzdGFydENyZWF0ZSgpXCJcbiAgICAgICAgPlxuICAgICAgICAgIDxtYXQtaWNvbj5hZGQ8L21hdC1pY29uPiBSdWxlXG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgPC9kaXY+XG5cbiAgICAgIEBpZiAoZWRpdGluZygpOyBhcyBkcmFmdCkge1xuICAgICAgICA8bmd4LXJ1bGUtZm9ybSBbcnVsZV09XCJkcmFmdFwiIChzYXZlKT1cImNvbW1pdCgkZXZlbnQpXCIgKGNhbmNlbCk9XCJlZGl0aW5nLnNldChudWxsKVwiIC8+XG4gICAgICB9XG5cbiAgICAgIEBpZiAoc3RvcmUucnVsZXNGb3JTZWxlY3RlZEZpZWxkKCkubGVuZ3RoID09PSAwICYmICFlZGl0aW5nKCkpIHtcbiAgICAgICAgPHAgY2xhc3M9XCJkZWItaGludFwiIGRhdGEtdGVzdGlkPVwicnVsZXMtZW1wdHlcIj5ObyBydWxlcyBvbiB0aGlzIGZpZWxkLjwvcD5cbiAgICAgIH1cblxuICAgICAgQGZvciAocnVsZSBvZiBzdG9yZS5ydWxlc0ZvclNlbGVjdGVkRmllbGQoKTsgdHJhY2sgcnVsZS5pZCkge1xuICAgICAgICA8ZGl2IGNsYXNzPVwiZGViLXJ1bGUtaXRlbVwiIFtjbGFzcy5kZWItcnVsZS1pdGVtLS1vZmZdPVwiIXJ1bGUuZW5hYmxlZFwiIGRhdGEtdGVzdGlkPVwicnVsZS1pdGVtXCI+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImRlYi1ydWxlLWl0ZW1fX2JvZHlcIj5cbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiZGViLXJ1bGUtaXRlbV9fc3VtbWFyeVwiPnt7IHN1bW1hcml6ZShydWxlKSB9fTwvc3Bhbj5cbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiZGViLXJ1bGUtaXRlbV9fbWV0YVwiPlxuICAgICAgICAgICAgICBwcmlvcml0eSB7eyBydWxlLnByaW9yaXR5IH19IMK3IHt7IHJ1bGUudGFyZ2V0cy5sZW5ndGggfX0gdGFyZ2V0KHMpXG4gICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImRlYi1ydWxlLWl0ZW1fX2FjdGlvbnNcIj5cbiAgICAgICAgICAgIDxtYXQtc2xpZGUtdG9nZ2xlXG4gICAgICAgICAgICAgIFtjaGVja2VkXT1cInJ1bGUuZW5hYmxlZFwiXG4gICAgICAgICAgICAgIG1hdFRvb2x0aXA9XCJFbmFibGUgLyBkaXNhYmxlXCJcbiAgICAgICAgICAgICAgKGNoYW5nZSk9XCJzdG9yZS50b2dnbGVSdWxlKHJ1bGUuaWQhLCAkZXZlbnQuY2hlY2tlZClcIlxuICAgICAgICAgICAgLz5cbiAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgbWF0LWljb24tYnV0dG9uXG4gICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgICBtYXRUb29sdGlwPVwiTW92ZSB1cFwiIGFyaWEtbGFiZWw9XCJNb3ZlIHJ1bGUgdXBcIlxuICAgICAgICAgICAgICBbYXR0ci5kYXRhLXRlc3RpZF09XCIncnVsZS11cC0nICsgcnVsZS5pZFwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJzdG9yZS5tb3ZlUnVsZShydWxlLmlkISwgLTEpXCJcbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgPG1hdC1pY29uPmFycm93X3Vwd2FyZDwvbWF0LWljb24+XG4gICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgbWF0LWljb24tYnV0dG9uXG4gICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgICBtYXRUb29sdGlwPVwiTW92ZSBkb3duXCIgYXJpYS1sYWJlbD1cIk1vdmUgcnVsZSBkb3duXCJcbiAgICAgICAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3J1bGUtZG93bi0nICsgcnVsZS5pZFwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJzdG9yZS5tb3ZlUnVsZShydWxlLmlkISwgMSlcIlxuICAgICAgICAgICAgPlxuICAgICAgICAgICAgICA8bWF0LWljb24+YXJyb3dfZG93bndhcmQ8L21hdC1pY29uPlxuICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgIG1hdC1pY29uLWJ1dHRvblxuICAgICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgICAgbWF0VG9vbHRpcD1cIkVkaXQgcnVsZVwiXG4gICAgICAgICAgICAgIFthdHRyLmRhdGEtdGVzdGlkXT1cIidydWxlLWVkaXQtJyArIHJ1bGUuaWRcIlxuICAgICAgICAgICAgICAoY2xpY2spPVwic3RhcnRFZGl0KHJ1bGUpXCJcbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgPG1hdC1pY29uPmVkaXQ8L21hdC1pY29uPlxuICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgIG1hdC1pY29uLWJ1dHRvblxuICAgICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgICAgY29sb3I9XCJ3YXJuXCJcbiAgICAgICAgICAgICAgbWF0VG9vbHRpcD1cIkRlbGV0ZSBydWxlXCJcbiAgICAgICAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3J1bGUtZGVsZXRlLScgKyBydWxlLmlkXCJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cInN0b3JlLnJlbW92ZVJ1bGUocnVsZS5pZCEpXCJcbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgPG1hdC1pY29uPmRlbGV0ZTwvbWF0LWljb24+XG4gICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgPC9kaXY+XG4gICAgICB9XG4gICAgPC9kaXY+XG4gIGAsXG4gIHN0eWxlczogW1xuICAgIGBcbiAgICAgIC5kZWItcnVsZXMge1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xuICAgICAgICBnYXA6IDhweDtcbiAgICAgIH1cbiAgICAgIC5kZWItcnVsZXNfX2hlYWQge1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICBhbGlnbi1pdGVtczogY2VudGVyO1xuICAgICAgICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47XG4gICAgICB9XG4gICAgICAuZGViLXJ1bGUtaXRlbSB7XG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gICAgICAgIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjtcbiAgICAgICAgZ2FwOiA4cHg7XG4gICAgICAgIGJvcmRlcjogMXB4IHNvbGlkICNlNWU3ZWI7XG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDhweDtcbiAgICAgICAgcGFkZGluZzogOHB4IDEwcHg7XG4gICAgICB9XG4gICAgICAuZGViLXJ1bGUtaXRlbS0tb2ZmIHtcbiAgICAgICAgb3BhY2l0eTogMC41NTtcbiAgICAgIH1cbiAgICAgIC5kZWItcnVsZS1pdGVtX19ib2R5IHtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcbiAgICAgICAgbWluLXdpZHRoOiAwO1xuICAgICAgfVxuICAgICAgLmRlYi1ydWxlLWl0ZW1fX3N1bW1hcnkge1xuICAgICAgICBmb250LXNpemU6IDEzcHg7XG4gICAgICAgIGZvbnQtd2VpZ2h0OiA2MDA7XG4gICAgICAgIG92ZXJmbG93LXdyYXA6IGFueXdoZXJlO1xuICAgICAgfVxuICAgICAgLmRlYi1ydWxlLWl0ZW1fX21ldGEge1xuICAgICAgICBmb250LXNpemU6IDExcHg7XG4gICAgICAgIGNvbG9yOiAjNmI3MjgwO1xuICAgICAgfVxuICAgICAgLmRlYi1ydWxlLWl0ZW1fX2FjdGlvbnMge1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICBhbGlnbi1pdGVtczogY2VudGVyO1xuICAgICAgICBmbGV4OiAwIDAgYXV0bztcbiAgICAgIH1cbiAgICBgLFxuICBdLFxufSlcbmV4cG9ydCBjbGFzcyBGaWVsZFJ1bGVzTGlzdENvbXBvbmVudCB7XG4gIHByb3RlY3RlZCByZWFkb25seSBzdG9yZSA9IGluamVjdChCdWlsZGVyU3RvcmUpO1xuXG4gIC8qKiBUaGUgcnVsZSBjdXJyZW50bHkgb3BlbiBpbiB0aGUgZWRpdG9yIOKAlCBhIG5ldyBkcmFmdCBvciBhIGNvcHkgb2YgYW4gZXhpc3RpbmcgcnVsZS4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGVkaXRpbmcgPSBzaWduYWw8Rm9ybVJ1bGUgfCBudWxsPihudWxsKTtcblxuICBwcm90ZWN0ZWQgc3RhcnRDcmVhdGUoKTogdm9pZCB7XG4gICAgY29uc3QgZmllbGRJZCA9IHRoaXMuc3RvcmUuc2VsZWN0ZWRGaWVsZElkKCk7XG4gICAgaWYgKCFmaWVsZElkKSByZXR1cm47XG4gICAgdGhpcy5lZGl0aW5nLnNldCh7XG4gICAgICBmb3JtQ29uZmlnSWQ6IHRoaXMuc3RvcmUuY29uZmlnKCkuZW50aXR5IHx8ICdmb3JtLTEnLFxuICAgICAgZmllbGRJZCxcbiAgICAgIGNvbmRpdGlvbnM6IFt7IG9wZXJhdG9yOiAnRVFVQUwnLCBjb21wYXJlVHlwZTogJ3ZhbHVlJywgdmFsdWU6ICcnIH1dLFxuICAgICAgYWN0aW9uOiB7IHR5cGU6ICd2aXNpYmlsaXR5JywgdmFsdWU6IGZhbHNlIH0sXG4gICAgICB0YXJnZXRzOiBbeyBpZDogZmllbGRJZCwgdHlwZTogJ2ZpZWxkJyB9XSxcbiAgICAgIGVuYWJsZWQ6IHRydWUsXG4gICAgICBwcmlvcml0eTogdGhpcy5zdG9yZS5ydWxlcygpLmxlbmd0aCArIDEsXG4gICAgfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3RhcnRFZGl0KHJ1bGU6IEZvcm1SdWxlKTogdm9pZCB7XG4gICAgdGhpcy5lZGl0aW5nLnNldChkZWVwQ2xvbmUocnVsZSkpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGNvbW1pdChydWxlOiBGb3JtUnVsZSk6IHZvaWQge1xuICAgIGlmIChydWxlLmlkKSB0aGlzLnN0b3JlLnVwZGF0ZVJ1bGUocnVsZS5pZCwgcnVsZSk7XG4gICAgZWxzZSB0aGlzLnN0b3JlLmFkZFJ1bGUocnVsZSk7XG4gICAgdGhpcy5lZGl0aW5nLnNldChudWxsKTtcbiAgfVxuXG4gIC8qKiBPbmUtbGluZSBodW1hbiBzdW1tYXJ5OiBgc3RhdHVzIEVRVUFMIFwiYXJjaGl2ZWRcIiDihpIgaGlkZWAuICovXG4gIHByb3RlY3RlZCBzdW1tYXJpemUocnVsZTogRm9ybVJ1bGUpOiBzdHJpbmcge1xuICAgIGNvbnN0IGNvbmQgPSBydWxlLmNvbmRpdGlvbnNcbiAgICAgIC5tYXAoYyA9PiB7XG4gICAgICAgIGNvbnN0IHJocyA9IGMuY29tcGFyZVR5cGUgPT09ICdmaWVsZCcgPyBjLmNvbXBhcmVUb0ZpZWxkIDogSlNPTi5zdHJpbmdpZnkoYy52YWx1ZSA/PyAnJyk7XG4gICAgICAgIHJldHVybiBgJHtjLm9wZXJhdG9yfSAke3JocyA/PyAnJ31gLnRyaW0oKTtcbiAgICAgIH0pXG4gICAgICAuam9pbignIEFORCAnKTtcblxuICAgIGNvbnN0IGFjdGlvbiA9XG4gICAgICBydWxlLmFjdGlvbi50eXBlID09PSAndmlzaWJpbGl0eSdcbiAgICAgICAgPyBydWxlLmFjdGlvbi52YWx1ZSA9PT0gZmFsc2UgfHwgcnVsZS5hY3Rpb24udmFsdWUgPT09ICdmYWxzZSdcbiAgICAgICAgICA/ICdoaWRlJ1xuICAgICAgICAgIDogJ3Nob3cnXG4gICAgICAgIDogYCR7cnVsZS5hY3Rpb24udHlwZX06ICR7cnVsZS5hY3Rpb24udmFsdWV9YDtcblxuICAgIHJldHVybiBgJHtydWxlLmZpZWxkSWR9ICR7Y29uZH0g4oaSICR7YWN0aW9ufWA7XG4gIH1cbn1cbiJdfQ==
|