@praxisui/metadata-editor 8.0.0-beta.2 → 8.0.0-beta.21
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 +35 -0
- package/fesm2022/praxisui-metadata-editor.mjs +2344 -91
- package/index.d.ts +26 -3
- package/package.json +10 -2
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, inject, Component, Input, ViewEncapsulation, EventEmitter, DestroyRef, Output, signal } from '@angular/core';
|
|
3
|
-
import * as
|
|
4
|
-
import { FormControl, FormGroup, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
5
|
-
import * as i3
|
|
2
|
+
import { Injectable, inject, Component, Input, ViewEncapsulation, EventEmitter, DestroyRef, Output, signal, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
3
|
+
import * as i2 from '@angular/forms';
|
|
4
|
+
import { FormControl, FormGroup, ReactiveFormsModule, FormArray, FormBuilder, FormsModule } from '@angular/forms';
|
|
5
|
+
import * as i3 from '@angular/common';
|
|
6
6
|
import { CommonModule } from '@angular/common';
|
|
7
7
|
import { DynamicFieldLoaderDirective } from '@praxisui/dynamic-fields';
|
|
8
|
-
import * as i2$
|
|
9
|
-
import { FieldControlType, PraxisIconDirective, INLINE_FILTER_CONTROL_TYPES, resolveInlineFilterControlType, normalizeControlTypeToken } from '@praxisui/core';
|
|
8
|
+
import * as i2$2 from '@praxisui/core';
|
|
9
|
+
import { FieldControlType, PraxisIconDirective, providePraxisI18n, PraxisI18nService, INLINE_FILTER_CONTROL_TYPES, resolveInlineFilterControlType, normalizeControlTypeToken, ComponentMetadataRegistry } from '@praxisui/core';
|
|
10
10
|
import * as i5 from '@angular/material/expansion';
|
|
11
11
|
import { MatExpansionModule } from '@angular/material/expansion';
|
|
12
12
|
import * as i6 from '@angular/material/icon';
|
|
@@ -17,7 +17,7 @@ import * as i1 from '@angular/material/dialog';
|
|
|
17
17
|
import { MatDialogRef, MatDialogModule } from '@angular/material/dialog';
|
|
18
18
|
import * as i8 from '@angular/material/tooltip';
|
|
19
19
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
20
|
-
import * as i2 from '@angular/material/list';
|
|
20
|
+
import * as i2$1 from '@angular/material/list';
|
|
21
21
|
import { MatListModule } from '@angular/material/list';
|
|
22
22
|
import { SETTINGS_PANEL_DATA } from '@praxisui/settings-panel';
|
|
23
23
|
import { BehaviorSubject } from 'rxjs';
|
|
@@ -276,13 +276,13 @@ class DynamicFormFactoryService {
|
|
|
276
276
|
}
|
|
277
277
|
return result;
|
|
278
278
|
}
|
|
279
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicFormFactoryService, deps: [{ token:
|
|
279
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicFormFactoryService, deps: [{ token: i2.FormBuilder }, { token: ContextValidatorRegistryService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
280
280
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicFormFactoryService, providedIn: 'root' });
|
|
281
281
|
}
|
|
282
282
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicFormFactoryService, decorators: [{
|
|
283
283
|
type: Injectable,
|
|
284
284
|
args: [{ providedIn: 'root' }]
|
|
285
|
-
}], ctorParameters: () => [{ type:
|
|
285
|
+
}], ctorParameters: () => [{ type: i2.FormBuilder }, { type: ContextValidatorRegistryService }] });
|
|
286
286
|
|
|
287
287
|
class EditorComponentRegistryService {
|
|
288
288
|
registry = new Map();
|
|
@@ -371,7 +371,7 @@ class RegexHelpDialogComponent {
|
|
|
371
371
|
<div mat-dialog-actions>
|
|
372
372
|
<button class="mat-mdc-button mdc-button" mat-dialog-close>Fechar</button>
|
|
373
373
|
</div>
|
|
374
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i2.MatNavList, selector: "mat-nav-list", exportAs: ["matNavList"] }, { kind: "component", type: i2.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "directive", type: i2.MatListItemLine, selector: "[matListItemLine]" }, { kind: "directive", type: i2.MatListItemTitle, selector: "[matListItemTitle]" }, { kind: "ngmodule", type: MatButtonModule }] });
|
|
374
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i2$1.MatNavList, selector: "mat-nav-list", exportAs: ["matNavList"] }, { kind: "component", type: i2$1.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "directive", type: i2$1.MatListItemLine, selector: "[matListItemLine]" }, { kind: "directive", type: i2$1.MatListItemTitle, selector: "[matListItemTitle]" }, { kind: "ngmodule", type: MatButtonModule }] });
|
|
375
375
|
}
|
|
376
376
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: RegexHelpDialogComponent, decorators: [{
|
|
377
377
|
type: Component,
|
|
@@ -683,7 +683,7 @@ class DynamicEditorRendererComponent {
|
|
|
683
683
|
this.form?.markAsDirty();
|
|
684
684
|
}
|
|
685
685
|
}
|
|
686
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicEditorRendererComponent, deps: [{ token: i1.MatDialog }, { token: i2$
|
|
686
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicEditorRendererComponent, deps: [{ token: i1.MatDialog }, { token: i2$2.IconPickerService }], target: i0.ɵɵFactoryTarget.Component });
|
|
687
687
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: DynamicEditorRendererComponent, isStandalone: true, selector: "praxis-dynamic-editor-renderer", inputs: { properties: "properties", form: "form" }, usesOnChanges: true, ngImport: i0, template: `
|
|
688
688
|
<ng-template #item let-field="field" let-index="index" let-content="content">
|
|
689
689
|
<div class="editor-item" [attr.data-control-type]="field.controlType">
|
|
@@ -753,7 +753,7 @@ class DynamicEditorRendererComponent {
|
|
|
753
753
|
</mat-expansion-panel>
|
|
754
754
|
</mat-accordion>
|
|
755
755
|
</div>
|
|
756
|
-
`, isInline: true, styles: [":host{display:block}.editor-surface{padding:8px 0 2px;border-radius:12px;background:transparent}.section-accordion{display:block}.section-card{margin:6px 0 8px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);overflow:hidden}.section-card.mat-expanded{border-color:var(--md-sys-color-primary);box-shadow:0 2px 10px #0000000d}.section-card .mat-expansion-panel-body{background:var(--md-sys-color-surface-container-low)}.section-header{background:var(--md-sys-color-surface-container-low);border-bottom:1px solid var(--md-sys-color-outline-variant, var(--sicoob-stroke-medium));padding:2px 8px;min-height:32px;display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-header .mat-expansion-panel-header-title,.section-header .mat-expansion-panel-header-description{display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-title{display:inline-flex;align-items:center;gap:6px;font-weight:600;letter-spacing:.2px;color:var(--md-sys-color-on-surface);font-size:.9rem;line-height:1.2}.section-icon{color:var(--md-sys-color-primary, var(--md-sys-color-primary, #3f51b5));opacity:.9}.editor-row{display:flex;gap:12px;align-items:flex-start;flex-wrap:wrap;margin-bottom:8px}.editor-row>praxis-field-shell{flex:1 1 100%;min-width:280px}.editor-row praxis-field-shell .mat-mdc-form-field{width:100%;margin-bottom:8px}.editor-row.row-inline>praxis-field-shell{flex:0 0 auto}.editor-row.row-2>praxis-field-shell{flex:1 1 calc(50% - 12px);min-width:260px}.editor-row.row-3>praxis-field-shell{flex:1 1 calc(33.333% - 12px);min-width:220px}.editor-row>praxis-field-shell[data-field-name=controlType],.editor-row>praxis-field-shell[data-field-name=label],.editor-row>praxis-field-shell[data-field-name=placeholder]{flex:1 1 calc(50% - 12px);min-width:300px}praxis-field-shell[data-field-name=label] .mdc-floating-label,praxis-field-shell[data-field-name=placeholder] .mdc-floating-label{font-weight:600}.editor-row>praxis-field-shell[data-field-name=prefix],.editor-row>praxis-field-shell[data-field-name=suffix]{flex:0 0 200px;min-width:160px}.editor-row>praxis-field-shell[data-field-name=prefixIcon],.editor-row>praxis-field-shell[data-field-name=suffixIcon],.editor-row>praxis-field-shell[data-field-name=prefixIconColor],.editor-row>praxis-field-shell[data-field-name=suffixIconColor]{flex:1 1 auto;min-width:160px}.editor-item{display:flex;align-items:flex-start;gap:4px}.editor-item>ng-container,.editor-item>praxis-field-shell{flex:1;min-width:0}.actions-container{display:flex;align-items:center;gap:0;margin-top:12px;flex-shrink:0}.decorator-btn{position:static;color:var(--md-sys-color-primary, #3f51b5);opacity:.6;transform:scale(.8);width:32px;height:32px;line-height:32px;padding:0}.decorator-btn:hover{opacity:1}.icon-picker-btn,.hint-btn,.hint-btn.has-icon-picker{right:auto}.editor-row .mat-mdc-form-field-subscript-wrapper mat-hint{display:none!important}.editor-row .mat-mdc-form-field-subscript-wrapper mat-error{display:block!important}praxis-field-shell[data-field-name=showCharCount] .mdc-switch{transform:scale(1.05)}praxis-field-shell[data-field-name=showCharCount] .mat-mdc-slide-toggle{font-weight:600}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint{display:flex;align-items:center;gap:8px;background:var(--md-sys-color-tertiary-container);border:1px solid var(--md-sys-color-tertiary);color:var(--md-sys-color-on-tertiary-container);padding:8px 10px;border-radius:6px;line-height:1.3;white-space:normal}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint:before{content:\"\\26a0\\fe0f\";display:inline-block}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-subscript-wrapper{margin-top:6px;margin-bottom:10px;min-height:auto}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field{margin-bottom:16px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3
|
|
756
|
+
`, isInline: true, styles: [":host{display:block}.editor-surface{padding:8px 0 2px;border-radius:12px;background:transparent}.section-accordion{display:block}.section-card{margin:6px 0 8px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);overflow:hidden}.section-card.mat-expanded{border-color:var(--md-sys-color-primary);box-shadow:0 2px 10px #0000000d}.section-card .mat-expansion-panel-body{background:var(--md-sys-color-surface-container-low)}.section-header{background:var(--md-sys-color-surface-container-low);border-bottom:1px solid var(--md-sys-color-outline-variant, var(--sicoob-stroke-medium));padding:2px 8px;min-height:32px;display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-header .mat-expansion-panel-header-title,.section-header .mat-expansion-panel-header-description{display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-title{display:inline-flex;align-items:center;gap:6px;font-weight:600;letter-spacing:.2px;color:var(--md-sys-color-on-surface);font-size:.9rem;line-height:1.2}.section-icon{color:var(--md-sys-color-primary, var(--md-sys-color-primary, #3f51b5));opacity:.9}.editor-row{display:flex;gap:12px;align-items:flex-start;flex-wrap:wrap;margin-bottom:8px}.editor-row>praxis-field-shell{flex:1 1 100%;min-width:280px}.editor-row praxis-field-shell .mat-mdc-form-field{width:100%;margin-bottom:8px}.editor-row.row-inline>praxis-field-shell{flex:0 0 auto}.editor-row.row-2>praxis-field-shell{flex:1 1 calc(50% - 12px);min-width:260px}.editor-row.row-3>praxis-field-shell{flex:1 1 calc(33.333% - 12px);min-width:220px}.editor-row>praxis-field-shell[data-field-name=controlType],.editor-row>praxis-field-shell[data-field-name=label],.editor-row>praxis-field-shell[data-field-name=placeholder]{flex:1 1 calc(50% - 12px);min-width:300px}praxis-field-shell[data-field-name=label] .mdc-floating-label,praxis-field-shell[data-field-name=placeholder] .mdc-floating-label{font-weight:600}.editor-row>praxis-field-shell[data-field-name=prefix],.editor-row>praxis-field-shell[data-field-name=suffix]{flex:0 0 200px;min-width:160px}.editor-row>praxis-field-shell[data-field-name=prefixIcon],.editor-row>praxis-field-shell[data-field-name=suffixIcon],.editor-row>praxis-field-shell[data-field-name=prefixIconColor],.editor-row>praxis-field-shell[data-field-name=suffixIconColor]{flex:1 1 auto;min-width:160px}.editor-item{display:flex;align-items:flex-start;gap:4px}.editor-item>ng-container,.editor-item>praxis-field-shell{flex:1;min-width:0}.actions-container{display:flex;align-items:center;gap:0;margin-top:12px;flex-shrink:0}.decorator-btn{position:static;color:var(--md-sys-color-primary, #3f51b5);opacity:.6;transform:scale(.8);width:32px;height:32px;line-height:32px;padding:0}.decorator-btn:hover{opacity:1}.icon-picker-btn,.hint-btn,.hint-btn.has-icon-picker{right:auto}.editor-row .mat-mdc-form-field-subscript-wrapper mat-hint{display:none!important}.editor-row .mat-mdc-form-field-subscript-wrapper mat-error{display:block!important}praxis-field-shell[data-field-name=showCharCount] .mdc-switch{transform:scale(1.05)}praxis-field-shell[data-field-name=showCharCount] .mat-mdc-slide-toggle{font-weight:600}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint{display:flex;align-items:center;gap:8px;background:var(--md-sys-color-tertiary-container);border:1px solid var(--md-sys-color-tertiary);color:var(--md-sys-color-on-tertiary-container);padding:8px 10px;border-radius:6px;line-height:1.3;white-space:normal}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint:before{content:\"\\26a0\\fe0f\";display:inline-block}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-subscript-wrapper{margin-top:6px;margin-bottom:10px;min-height:auto}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field{margin-bottom:16px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: DynamicFieldLoaderDirective, selector: "[dynamicFieldLoader]", inputs: ["fields", "formGroup", "enableExternalControlBinding", "itemTemplate", "debugTrace", "debugTraceLabel", "readonlyMode", "disabledMode", "presentationMode", "visible", "canvasMode"], outputs: ["componentsCreated", "fieldCreated", "fieldDestroyed", "renderError", "canvasMouseEnter", "canvasMouseLeave", "canvasClick"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i5.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i5.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i5.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i5.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
757
757
|
}
|
|
758
758
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicEditorRendererComponent, decorators: [{
|
|
759
759
|
type: Component,
|
|
@@ -837,12 +837,837 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
837
837
|
</mat-accordion>
|
|
838
838
|
</div>
|
|
839
839
|
`, styles: [":host{display:block}.editor-surface{padding:8px 0 2px;border-radius:12px;background:transparent}.section-accordion{display:block}.section-card{margin:6px 0 8px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);overflow:hidden}.section-card.mat-expanded{border-color:var(--md-sys-color-primary);box-shadow:0 2px 10px #0000000d}.section-card .mat-expansion-panel-body{background:var(--md-sys-color-surface-container-low)}.section-header{background:var(--md-sys-color-surface-container-low);border-bottom:1px solid var(--md-sys-color-outline-variant, var(--sicoob-stroke-medium));padding:2px 8px;min-height:32px;display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-header .mat-expansion-panel-header-title,.section-header .mat-expansion-panel-header-description{display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-title{display:inline-flex;align-items:center;gap:6px;font-weight:600;letter-spacing:.2px;color:var(--md-sys-color-on-surface);font-size:.9rem;line-height:1.2}.section-icon{color:var(--md-sys-color-primary, var(--md-sys-color-primary, #3f51b5));opacity:.9}.editor-row{display:flex;gap:12px;align-items:flex-start;flex-wrap:wrap;margin-bottom:8px}.editor-row>praxis-field-shell{flex:1 1 100%;min-width:280px}.editor-row praxis-field-shell .mat-mdc-form-field{width:100%;margin-bottom:8px}.editor-row.row-inline>praxis-field-shell{flex:0 0 auto}.editor-row.row-2>praxis-field-shell{flex:1 1 calc(50% - 12px);min-width:260px}.editor-row.row-3>praxis-field-shell{flex:1 1 calc(33.333% - 12px);min-width:220px}.editor-row>praxis-field-shell[data-field-name=controlType],.editor-row>praxis-field-shell[data-field-name=label],.editor-row>praxis-field-shell[data-field-name=placeholder]{flex:1 1 calc(50% - 12px);min-width:300px}praxis-field-shell[data-field-name=label] .mdc-floating-label,praxis-field-shell[data-field-name=placeholder] .mdc-floating-label{font-weight:600}.editor-row>praxis-field-shell[data-field-name=prefix],.editor-row>praxis-field-shell[data-field-name=suffix]{flex:0 0 200px;min-width:160px}.editor-row>praxis-field-shell[data-field-name=prefixIcon],.editor-row>praxis-field-shell[data-field-name=suffixIcon],.editor-row>praxis-field-shell[data-field-name=prefixIconColor],.editor-row>praxis-field-shell[data-field-name=suffixIconColor]{flex:1 1 auto;min-width:160px}.editor-item{display:flex;align-items:flex-start;gap:4px}.editor-item>ng-container,.editor-item>praxis-field-shell{flex:1;min-width:0}.actions-container{display:flex;align-items:center;gap:0;margin-top:12px;flex-shrink:0}.decorator-btn{position:static;color:var(--md-sys-color-primary, #3f51b5);opacity:.6;transform:scale(.8);width:32px;height:32px;line-height:32px;padding:0}.decorator-btn:hover{opacity:1}.icon-picker-btn,.hint-btn,.hint-btn.has-icon-picker{right:auto}.editor-row .mat-mdc-form-field-subscript-wrapper mat-hint{display:none!important}.editor-row .mat-mdc-form-field-subscript-wrapper mat-error{display:block!important}praxis-field-shell[data-field-name=showCharCount] .mdc-switch{transform:scale(1.05)}praxis-field-shell[data-field-name=showCharCount] .mat-mdc-slide-toggle{font-weight:600}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint{display:flex;align-items:center;gap:8px;background:var(--md-sys-color-tertiary-container);border:1px solid var(--md-sys-color-tertiary);color:var(--md-sys-color-on-tertiary-container);padding:8px 10px;border-radius:6px;line-height:1.3;white-space:normal}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint:before{content:\"\\26a0\\fe0f\";display:inline-block}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-subscript-wrapper{margin-top:6px;margin-bottom:10px;min-height:auto}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field{margin-bottom:16px}\n"] }]
|
|
840
|
-
}], ctorParameters: () => [{ type: i1.MatDialog }, { type: i2$
|
|
840
|
+
}], ctorParameters: () => [{ type: i1.MatDialog }, { type: i2$2.IconPickerService }], propDecorators: { properties: [{
|
|
841
841
|
type: Input
|
|
842
842
|
}], form: [{
|
|
843
843
|
type: Input
|
|
844
844
|
}] } });
|
|
845
845
|
|
|
846
|
+
const METADATA_EDITOR_EN_US = {
|
|
847
|
+
'praxis.metadataEditor.arrayFields.section': 'Collection subfields',
|
|
848
|
+
'praxis.metadataEditor.arrayFields.title': 'Subfields',
|
|
849
|
+
'praxis.metadataEditor.arrayFields.add': 'Add subfield',
|
|
850
|
+
'praxis.metadataEditor.arrayFields.duplicateNames': 'There are duplicate names: {{names}}',
|
|
851
|
+
'praxis.metadataEditor.arrayFields.empty': 'No local subfields configured.',
|
|
852
|
+
'praxis.metadataEditor.arrayFields.name': 'Name',
|
|
853
|
+
'praxis.metadataEditor.arrayFields.nameRequired': 'Enter the subfield name.',
|
|
854
|
+
'praxis.metadataEditor.arrayFields.nameDuplicated': 'This name is already used in the collection.',
|
|
855
|
+
'praxis.metadataEditor.arrayFields.label': 'Label',
|
|
856
|
+
'praxis.metadataEditor.arrayFields.controlType': 'Control',
|
|
857
|
+
'praxis.metadataEditor.arrayFields.required': 'Required',
|
|
858
|
+
'praxis.metadataEditor.arrayFields.moveUp': 'Move up',
|
|
859
|
+
'praxis.metadataEditor.arrayFields.moveDown': 'Move down',
|
|
860
|
+
'praxis.metadataEditor.arrayFields.duplicate': 'Duplicate',
|
|
861
|
+
'praxis.metadataEditor.arrayFields.remove': 'Remove',
|
|
862
|
+
'praxis.metadataEditor.arrayFields.removeBlocked': 'Remove references before deleting: {{references}}',
|
|
863
|
+
'praxis.metadataEditor.arrayFields.refOnly': 'The item schema comes from itemSchemaRef; create local subfields only when you need an override.',
|
|
864
|
+
'praxis.metadataEditor.arrayFields.overrideLocal': 'There is an itemSchemaRef and local subfields; local subfields work as an explicit override.',
|
|
865
|
+
'praxis.metadataEditor.arrayFields.imported': 'Subfields imported. Review them before applying or saving.',
|
|
866
|
+
'praxis.metadataEditor.arrayFields.localOnly': 'Local subfields will be saved in array.itemSchema.fields.',
|
|
867
|
+
'praxis.metadataEditor.arrayFields.control.input': 'Text',
|
|
868
|
+
'praxis.metadataEditor.arrayFields.control.textarea': 'Long text',
|
|
869
|
+
'praxis.metadataEditor.arrayFields.control.numeric': 'Number',
|
|
870
|
+
'praxis.metadataEditor.arrayFields.control.currency': 'Currency',
|
|
871
|
+
'praxis.metadataEditor.arrayFields.control.email': 'Email',
|
|
872
|
+
'praxis.metadataEditor.arrayFields.control.phone': 'Phone',
|
|
873
|
+
'praxis.metadataEditor.arrayFields.control.select': 'Select',
|
|
874
|
+
'praxis.metadataEditor.arrayFields.control.entityLookup': 'Entity lookup',
|
|
875
|
+
'praxis.metadataEditor.arrayFields.control.toggle': 'Toggle',
|
|
876
|
+
'praxis.metadataEditor.arrayFields.control.date': 'Date',
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
const METADATA_EDITOR_PT_BR = {
|
|
880
|
+
'praxis.metadataEditor.arrayFields.section': 'Subcampos da cole\u00e7\u00e3o',
|
|
881
|
+
'praxis.metadataEditor.arrayFields.title': 'Subcampos',
|
|
882
|
+
'praxis.metadataEditor.arrayFields.add': 'Adicionar subcampo',
|
|
883
|
+
'praxis.metadataEditor.arrayFields.duplicateNames': 'Existem nomes duplicados: {{names}}',
|
|
884
|
+
'praxis.metadataEditor.arrayFields.empty': 'Nenhum subcampo local configurado.',
|
|
885
|
+
'praxis.metadataEditor.arrayFields.name': 'Nome',
|
|
886
|
+
'praxis.metadataEditor.arrayFields.nameRequired': 'Informe o nome do subcampo.',
|
|
887
|
+
'praxis.metadataEditor.arrayFields.nameDuplicated': 'Este nome j\u00e1 est\u00e1 em uso na cole\u00e7\u00e3o.',
|
|
888
|
+
'praxis.metadataEditor.arrayFields.label': 'Label',
|
|
889
|
+
'praxis.metadataEditor.arrayFields.controlType': 'Controle',
|
|
890
|
+
'praxis.metadataEditor.arrayFields.required': 'Obrigat\u00f3rio',
|
|
891
|
+
'praxis.metadataEditor.arrayFields.moveUp': 'Subir',
|
|
892
|
+
'praxis.metadataEditor.arrayFields.moveDown': 'Descer',
|
|
893
|
+
'praxis.metadataEditor.arrayFields.duplicate': 'Duplicar',
|
|
894
|
+
'praxis.metadataEditor.arrayFields.remove': 'Remover',
|
|
895
|
+
'praxis.metadataEditor.arrayFields.removeBlocked': 'Remova as refer\u00eancias antes de excluir: {{references}}',
|
|
896
|
+
'praxis.metadataEditor.arrayFields.refOnly': 'O schema do item vem do itemSchemaRef; crie subcampos locais apenas quando precisar de override.',
|
|
897
|
+
'praxis.metadataEditor.arrayFields.overrideLocal': 'H\u00e1 um itemSchemaRef e subcampos locais; os subcampos locais funcionam como override expl\u00edcito.',
|
|
898
|
+
'praxis.metadataEditor.arrayFields.imported': 'Subcampos importados. Revise antes de aplicar ou salvar.',
|
|
899
|
+
'praxis.metadataEditor.arrayFields.localOnly': 'Subcampos locais ser\u00e3o salvos em array.itemSchema.fields.',
|
|
900
|
+
'praxis.metadataEditor.arrayFields.control.input': 'Texto',
|
|
901
|
+
'praxis.metadataEditor.arrayFields.control.textarea': 'Texto longo',
|
|
902
|
+
'praxis.metadataEditor.arrayFields.control.numeric': 'N\u00famero',
|
|
903
|
+
'praxis.metadataEditor.arrayFields.control.currency': 'Moeda',
|
|
904
|
+
'praxis.metadataEditor.arrayFields.control.email': 'E-mail',
|
|
905
|
+
'praxis.metadataEditor.arrayFields.control.phone': 'Telefone',
|
|
906
|
+
'praxis.metadataEditor.arrayFields.control.select': 'Sele\u00e7\u00e3o',
|
|
907
|
+
'praxis.metadataEditor.arrayFields.control.entityLookup': 'Busca de entidade',
|
|
908
|
+
'praxis.metadataEditor.arrayFields.control.toggle': 'Liga/desliga',
|
|
909
|
+
'praxis.metadataEditor.arrayFields.control.date': 'Data',
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
function createPraxisMetadataEditorI18nConfig(options = {}) {
|
|
913
|
+
const dictionaries = {
|
|
914
|
+
'pt-BR': {
|
|
915
|
+
...METADATA_EDITOR_PT_BR,
|
|
916
|
+
...(options.dictionaries?.['pt-BR'] ?? {}),
|
|
917
|
+
},
|
|
918
|
+
'en-US': {
|
|
919
|
+
...METADATA_EDITOR_EN_US,
|
|
920
|
+
...(options.dictionaries?.['en-US'] ?? {}),
|
|
921
|
+
},
|
|
922
|
+
};
|
|
923
|
+
for (const [locale, dictionary] of Object.entries(options.dictionaries ?? {})) {
|
|
924
|
+
if (locale === 'pt-BR' || locale === 'en-US') {
|
|
925
|
+
continue;
|
|
926
|
+
}
|
|
927
|
+
dictionaries[locale] = {
|
|
928
|
+
...(dictionaries[locale] ?? {}),
|
|
929
|
+
...dictionary,
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
return {
|
|
933
|
+
locale: options.locale,
|
|
934
|
+
fallbackLocale: options.fallbackLocale ?? 'pt-BR',
|
|
935
|
+
dictionaries,
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
const METADATA_EDITOR_I18N_CONFIG = createPraxisMetadataEditorI18nConfig();
|
|
939
|
+
function providePraxisMetadataEditorI18n(options = {}) {
|
|
940
|
+
return providePraxisI18n(createPraxisMetadataEditorI18nConfig(options));
|
|
941
|
+
}
|
|
942
|
+
function resolvePraxisMetadataEditorText(i18n, key, fallback) {
|
|
943
|
+
const namespacedKey = key.startsWith('praxis.metadataEditor.')
|
|
944
|
+
? key
|
|
945
|
+
: `praxis.metadataEditor.${key}`;
|
|
946
|
+
return i18n.t(namespacedKey, undefined, fallback) || fallback;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
class ArrayItemSchemaFieldsEditorComponent {
|
|
950
|
+
static nextInstanceId = 0;
|
|
951
|
+
control = null;
|
|
952
|
+
arrayControl = null;
|
|
953
|
+
formArray = new FormArray([]);
|
|
954
|
+
form = new FormGroup({ fields: this.formArray });
|
|
955
|
+
controlTypeOptions = [
|
|
956
|
+
FieldControlType.INPUT,
|
|
957
|
+
FieldControlType.TEXTAREA,
|
|
958
|
+
FieldControlType.NUMERIC_TEXT_BOX,
|
|
959
|
+
FieldControlType.CURRENCY_INPUT,
|
|
960
|
+
FieldControlType.EMAIL_INPUT,
|
|
961
|
+
FieldControlType.PHONE,
|
|
962
|
+
FieldControlType.SELECT,
|
|
963
|
+
FieldControlType.ENTITY_LOOKUP,
|
|
964
|
+
FieldControlType.TOGGLE,
|
|
965
|
+
FieldControlType.DATE_INPUT,
|
|
966
|
+
];
|
|
967
|
+
schemaSourceState = 'local-only';
|
|
968
|
+
duplicateNames = [];
|
|
969
|
+
fb = inject(FormBuilder);
|
|
970
|
+
i18n = inject(PraxisI18nService);
|
|
971
|
+
instanceId = ++ArrayItemSchemaFieldsEditorComponent.nextInstanceId;
|
|
972
|
+
externalSubscription;
|
|
973
|
+
internalSubscription;
|
|
974
|
+
originalNames = [];
|
|
975
|
+
importedFieldsFingerprint = null;
|
|
976
|
+
hydrating = false;
|
|
977
|
+
get rows() {
|
|
978
|
+
return this.formArray.controls;
|
|
979
|
+
}
|
|
980
|
+
ngOnChanges(changes) {
|
|
981
|
+
if (changes['control']) {
|
|
982
|
+
this.externalSubscription?.unsubscribe();
|
|
983
|
+
this.hydrateFromControl();
|
|
984
|
+
this.externalSubscription = this.control?.valueChanges.subscribe(() => {
|
|
985
|
+
if (!this.hydrating) {
|
|
986
|
+
this.hydrateFromControl();
|
|
987
|
+
}
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
if (changes['arrayControl'] || changes['control']) {
|
|
991
|
+
this.updateSchemaSourceState();
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
ngOnDestroy() {
|
|
995
|
+
this.externalSubscription?.unsubscribe();
|
|
996
|
+
this.internalSubscription?.unsubscribe();
|
|
997
|
+
}
|
|
998
|
+
text(key, fallback, fallbackWithParams, params) {
|
|
999
|
+
return this.i18n.t(key, params, fallbackWithParams ?? fallback);
|
|
1000
|
+
}
|
|
1001
|
+
controlTypeLabel(controlType) {
|
|
1002
|
+
const labels = {
|
|
1003
|
+
[FieldControlType.INPUT]: this.text('praxis.metadataEditor.arrayFields.control.input', 'Texto'),
|
|
1004
|
+
[FieldControlType.TEXTAREA]: this.text('praxis.metadataEditor.arrayFields.control.textarea', 'Texto longo'),
|
|
1005
|
+
[FieldControlType.NUMERIC_TEXT_BOX]: this.text('praxis.metadataEditor.arrayFields.control.numeric', 'N\u00famero'),
|
|
1006
|
+
[FieldControlType.CURRENCY_INPUT]: this.text('praxis.metadataEditor.arrayFields.control.currency', 'Moeda'),
|
|
1007
|
+
[FieldControlType.EMAIL_INPUT]: this.text('praxis.metadataEditor.arrayFields.control.email', 'E-mail'),
|
|
1008
|
+
[FieldControlType.PHONE]: this.text('praxis.metadataEditor.arrayFields.control.phone', 'Telefone'),
|
|
1009
|
+
[FieldControlType.SELECT]: this.text('praxis.metadataEditor.arrayFields.control.select', 'Sele\u00e7\u00e3o'),
|
|
1010
|
+
[FieldControlType.ENTITY_LOOKUP]: this.text('praxis.metadataEditor.arrayFields.control.entityLookup', 'Busca de entidade'),
|
|
1011
|
+
[FieldControlType.TOGGLE]: this.text('praxis.metadataEditor.arrayFields.control.toggle', 'Liga/desliga'),
|
|
1012
|
+
[FieldControlType.DATE_INPUT]: this.text('praxis.metadataEditor.arrayFields.control.date', 'Data'),
|
|
1013
|
+
};
|
|
1014
|
+
return labels[controlType] ?? controlType;
|
|
1015
|
+
}
|
|
1016
|
+
addField(seed = {}) {
|
|
1017
|
+
const fieldName = seed.name || this.nextAvailableName();
|
|
1018
|
+
this.formArray.push(this.createRow({
|
|
1019
|
+
...seed,
|
|
1020
|
+
name: fieldName,
|
|
1021
|
+
label: seed.label || fieldName,
|
|
1022
|
+
controlType: seed.controlType || FieldControlType.INPUT,
|
|
1023
|
+
required: seed.required ?? false,
|
|
1024
|
+
}));
|
|
1025
|
+
this.originalNames.push(fieldName);
|
|
1026
|
+
this.syncToControl();
|
|
1027
|
+
}
|
|
1028
|
+
duplicateField(index) {
|
|
1029
|
+
const current = this.rowValue(index);
|
|
1030
|
+
if (!current)
|
|
1031
|
+
return;
|
|
1032
|
+
const nextName = this.nextAvailableName(`${current.name || 'field'}Copy`);
|
|
1033
|
+
this.addField({
|
|
1034
|
+
...current,
|
|
1035
|
+
name: nextName,
|
|
1036
|
+
label: nextName,
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
removeField(index) {
|
|
1040
|
+
const name = String(this.formArray.at(index)?.get('name')?.value || '').trim();
|
|
1041
|
+
if (this.isReferenced(name)) {
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
this.formArray.removeAt(index);
|
|
1045
|
+
this.originalNames.splice(index, 1);
|
|
1046
|
+
this.syncToControl();
|
|
1047
|
+
}
|
|
1048
|
+
moveField(index, delta) {
|
|
1049
|
+
const target = index + delta;
|
|
1050
|
+
if (target < 0 || target >= this.formArray.length)
|
|
1051
|
+
return;
|
|
1052
|
+
const row = this.formArray.at(index);
|
|
1053
|
+
this.formArray.removeAt(index);
|
|
1054
|
+
this.formArray.insert(target, row);
|
|
1055
|
+
const [originalName] = this.originalNames.splice(index, 1);
|
|
1056
|
+
this.originalNames.splice(target, 0, originalName);
|
|
1057
|
+
this.syncToControl();
|
|
1058
|
+
}
|
|
1059
|
+
onNameBlur(index) {
|
|
1060
|
+
const previous = this.originalNames[index];
|
|
1061
|
+
const next = String(this.formArray.at(index)?.get('name')?.value || '').trim();
|
|
1062
|
+
if (previous && !next && this.isReferenced(previous)) {
|
|
1063
|
+
this.formArray.at(index)?.get('name')?.setValue(previous, { emitEvent: false });
|
|
1064
|
+
this.syncToControl();
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
if (previous && next && previous !== next) {
|
|
1068
|
+
this.renameReferences(previous, next);
|
|
1069
|
+
this.originalNames[index] = next;
|
|
1070
|
+
}
|
|
1071
|
+
this.syncToControl();
|
|
1072
|
+
}
|
|
1073
|
+
importFieldsFromJson(raw) {
|
|
1074
|
+
try {
|
|
1075
|
+
const parsed = JSON.parse(String(raw || '').trim());
|
|
1076
|
+
if (!Array.isArray(parsed) ||
|
|
1077
|
+
parsed.some((entry) => !this.isImportableField(entry))) {
|
|
1078
|
+
return false;
|
|
1079
|
+
}
|
|
1080
|
+
this.replaceRows(parsed);
|
|
1081
|
+
this.schemaSourceState = 'imported';
|
|
1082
|
+
this.importedFieldsFingerprint = this.fieldsFingerprint();
|
|
1083
|
+
this.syncToControl();
|
|
1084
|
+
return true;
|
|
1085
|
+
}
|
|
1086
|
+
catch {
|
|
1087
|
+
return false;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
isImportableField(value) {
|
|
1091
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
1092
|
+
return false;
|
|
1093
|
+
}
|
|
1094
|
+
const raw = value;
|
|
1095
|
+
return typeof raw.name === 'string' && raw.name.trim().length > 0;
|
|
1096
|
+
}
|
|
1097
|
+
isNameDuplicated(index) {
|
|
1098
|
+
const name = String(this.formArray.at(index)?.get('name')?.value || '').trim();
|
|
1099
|
+
return !!name && this.duplicateNames.includes(name);
|
|
1100
|
+
}
|
|
1101
|
+
isNameInvalid(index) {
|
|
1102
|
+
const control = this.formArray.at(index)?.get('name');
|
|
1103
|
+
return !!control?.invalid;
|
|
1104
|
+
}
|
|
1105
|
+
nameErrorId(index) {
|
|
1106
|
+
return `praxis-array-field-name-error-${this.instanceId}-${index}`;
|
|
1107
|
+
}
|
|
1108
|
+
nameErrorDescribedBy(index) {
|
|
1109
|
+
return this.nameErrorMessage(index) ? this.nameErrorId(index) : null;
|
|
1110
|
+
}
|
|
1111
|
+
nameErrorMessage(index) {
|
|
1112
|
+
const control = this.formArray.at(index)?.get('name');
|
|
1113
|
+
if (!control?.errors) {
|
|
1114
|
+
return '';
|
|
1115
|
+
}
|
|
1116
|
+
if (control.errors['arrayFieldNameRequired']) {
|
|
1117
|
+
return this.text('praxis.metadataEditor.arrayFields.nameRequired', 'Informe o nome do subcampo.');
|
|
1118
|
+
}
|
|
1119
|
+
if (control.errors['arrayFieldNameDuplicated']) {
|
|
1120
|
+
return this.text('praxis.metadataEditor.arrayFields.nameDuplicated', 'Este nome j\u00e1 est\u00e1 em uso na cole\u00e7\u00e3o.');
|
|
1121
|
+
}
|
|
1122
|
+
return '';
|
|
1123
|
+
}
|
|
1124
|
+
isReferenced(fieldName) {
|
|
1125
|
+
return this.referencedBy(fieldName).length > 0;
|
|
1126
|
+
}
|
|
1127
|
+
removeTitle(fieldName) {
|
|
1128
|
+
const references = this.referencedBy(fieldName);
|
|
1129
|
+
if (!references.length) {
|
|
1130
|
+
return '';
|
|
1131
|
+
}
|
|
1132
|
+
return this.text('praxis.metadataEditor.arrayFields.removeBlocked', undefined, 'Remova as refer\u00eancias antes de excluir: {{references}}', { references: references.join(', ') });
|
|
1133
|
+
}
|
|
1134
|
+
schemaStateDescription() {
|
|
1135
|
+
switch (this.schemaSourceState) {
|
|
1136
|
+
case 'ref-only':
|
|
1137
|
+
return this.text('praxis.metadataEditor.arrayFields.refOnly', 'O schema do item vem do itemSchemaRef; crie subcampos locais apenas quando precisar de override.');
|
|
1138
|
+
case 'override-local':
|
|
1139
|
+
return this.text('praxis.metadataEditor.arrayFields.overrideLocal', 'H\u00e1 um itemSchemaRef e subcampos locais; os subcampos locais funcionam como override expl\u00edcito.');
|
|
1140
|
+
case 'imported':
|
|
1141
|
+
return this.text('praxis.metadataEditor.arrayFields.imported', 'Subcampos importados. Revise antes de aplicar ou salvar.');
|
|
1142
|
+
default:
|
|
1143
|
+
return this.text('praxis.metadataEditor.arrayFields.localOnly', 'Subcampos locais ser\u00e3o salvos em array.itemSchema.fields.');
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
hydrateFromControl() {
|
|
1147
|
+
const fields = this.normalizeFields(this.control?.value);
|
|
1148
|
+
this.replaceRows(fields, false);
|
|
1149
|
+
this.updateSchemaSourceState();
|
|
1150
|
+
}
|
|
1151
|
+
replaceRows(fields, emit = true) {
|
|
1152
|
+
this.hydrating = true;
|
|
1153
|
+
this.internalSubscription?.unsubscribe();
|
|
1154
|
+
while (this.formArray.length) {
|
|
1155
|
+
this.formArray.removeAt(0, { emitEvent: false });
|
|
1156
|
+
}
|
|
1157
|
+
this.originalNames = [];
|
|
1158
|
+
for (const field of fields) {
|
|
1159
|
+
const normalized = this.normalizeField(field);
|
|
1160
|
+
this.formArray.push(this.createRow(normalized), { emitEvent: false });
|
|
1161
|
+
this.originalNames.push(normalized.name);
|
|
1162
|
+
}
|
|
1163
|
+
this.internalSubscription = this.formArray.valueChanges.subscribe(() => this.syncToControl());
|
|
1164
|
+
this.hydrating = false;
|
|
1165
|
+
this.updateFieldListValidity();
|
|
1166
|
+
if (emit) {
|
|
1167
|
+
this.syncToControl();
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
createRow(field) {
|
|
1171
|
+
return this.fb.group({
|
|
1172
|
+
name: [field.name || ''],
|
|
1173
|
+
label: [field.label || field.name || ''],
|
|
1174
|
+
controlType: [field.controlType || FieldControlType.INPUT],
|
|
1175
|
+
required: [!!field.required],
|
|
1176
|
+
metadata: [this.clonePlainObject(field)],
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
rowValue(index) {
|
|
1180
|
+
const row = this.formArray.at(index);
|
|
1181
|
+
return row ? this.normalizeField(row.getRawValue()) : null;
|
|
1182
|
+
}
|
|
1183
|
+
syncToControl() {
|
|
1184
|
+
if (this.hydrating || !this.control)
|
|
1185
|
+
return;
|
|
1186
|
+
const normalizedRows = this.formArray.controls.map((row) => this.normalizeField(row.getRawValue()));
|
|
1187
|
+
this.updateDuplicateNames(normalizedRows);
|
|
1188
|
+
const fields = normalizedRows.filter((field) => field.name);
|
|
1189
|
+
this.hydrating = true;
|
|
1190
|
+
this.control.setValue(fields, { emitEvent: true });
|
|
1191
|
+
this.control.markAsDirty();
|
|
1192
|
+
this.control.markAsTouched();
|
|
1193
|
+
this.hydrating = false;
|
|
1194
|
+
this.updateFieldListValidity(normalizedRows);
|
|
1195
|
+
this.updateSchemaSourceState();
|
|
1196
|
+
}
|
|
1197
|
+
normalizeFields(value) {
|
|
1198
|
+
if (Array.isArray(value)) {
|
|
1199
|
+
return value;
|
|
1200
|
+
}
|
|
1201
|
+
if (typeof value === 'string' && value.trim()) {
|
|
1202
|
+
try {
|
|
1203
|
+
const parsed = JSON.parse(value);
|
|
1204
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
1205
|
+
}
|
|
1206
|
+
catch {
|
|
1207
|
+
return [];
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
return [];
|
|
1211
|
+
}
|
|
1212
|
+
normalizeField(value) {
|
|
1213
|
+
const raw = (value && typeof value === 'object' ? value : {});
|
|
1214
|
+
const base = raw.metadata && typeof raw.metadata === 'object' && !Array.isArray(raw.metadata)
|
|
1215
|
+
? this.clonePlainObject(raw.metadata)
|
|
1216
|
+
: this.clonePlainObject(raw);
|
|
1217
|
+
const name = String(raw.name ?? base.name ?? '').trim();
|
|
1218
|
+
return {
|
|
1219
|
+
...base,
|
|
1220
|
+
name,
|
|
1221
|
+
label: String(raw.label ?? base.label ?? name ?? '').trim(),
|
|
1222
|
+
controlType: raw.controlType || base.controlType || FieldControlType.INPUT,
|
|
1223
|
+
required: raw.required === true,
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
nextAvailableName(prefix = 'campo') {
|
|
1227
|
+
const existing = new Set(this.formArray.controls
|
|
1228
|
+
.map((row) => String(row.get('name')?.value || '').trim())
|
|
1229
|
+
.filter(Boolean));
|
|
1230
|
+
let index = 1;
|
|
1231
|
+
let candidate = prefix;
|
|
1232
|
+
while (existing.has(candidate)) {
|
|
1233
|
+
index += 1;
|
|
1234
|
+
candidate = `${prefix}${index}`;
|
|
1235
|
+
}
|
|
1236
|
+
return candidate;
|
|
1237
|
+
}
|
|
1238
|
+
updateDuplicateNames(fields) {
|
|
1239
|
+
const source = fields ?? this.formArray.controls.map((row) => this.normalizeField(row.getRawValue()));
|
|
1240
|
+
const seen = new Set();
|
|
1241
|
+
const duplicated = new Set();
|
|
1242
|
+
for (const field of source) {
|
|
1243
|
+
const name = String(field.name || '').trim();
|
|
1244
|
+
if (!name)
|
|
1245
|
+
continue;
|
|
1246
|
+
if (seen.has(name)) {
|
|
1247
|
+
duplicated.add(name);
|
|
1248
|
+
}
|
|
1249
|
+
seen.add(name);
|
|
1250
|
+
}
|
|
1251
|
+
this.duplicateNames = Array.from(duplicated);
|
|
1252
|
+
}
|
|
1253
|
+
updateFieldListValidity(fields = this.formArray.controls.map((row) => this.normalizeField(row.getRawValue()))) {
|
|
1254
|
+
this.updateDuplicateNames(fields);
|
|
1255
|
+
const duplicateSet = new Set(this.duplicateNames);
|
|
1256
|
+
const hasRequiredNameError = fields.some((field) => !String(field.name || '').trim());
|
|
1257
|
+
const hasDuplicateNameError = duplicateSet.size > 0;
|
|
1258
|
+
this.formArray.controls.forEach((row) => {
|
|
1259
|
+
const nameControl = row.get('name');
|
|
1260
|
+
const name = String(nameControl?.value || '').trim();
|
|
1261
|
+
this.setControlError(nameControl, 'arrayFieldNameRequired', !name);
|
|
1262
|
+
this.setControlError(nameControl, 'arrayFieldNameDuplicated', !!name && duplicateSet.has(name));
|
|
1263
|
+
});
|
|
1264
|
+
const errorPayload = hasDuplicateNameError ? { names: this.duplicateNames } : null;
|
|
1265
|
+
this.setControlError(this.control, 'arrayFieldNameRequired', hasRequiredNameError);
|
|
1266
|
+
this.setControlError(this.control, 'arrayFieldNameDuplicated', errorPayload);
|
|
1267
|
+
this.setControlError(this.formArray, 'arrayFieldNameRequired', hasRequiredNameError);
|
|
1268
|
+
this.setControlError(this.formArray, 'arrayFieldNameDuplicated', errorPayload);
|
|
1269
|
+
}
|
|
1270
|
+
setControlError(control, key, value) {
|
|
1271
|
+
if (!control)
|
|
1272
|
+
return;
|
|
1273
|
+
const errors = { ...(control.errors || {}) };
|
|
1274
|
+
const hasError = value !== null && value !== undefined && value !== false;
|
|
1275
|
+
if (hasError) {
|
|
1276
|
+
errors[key] = value === true ? true : value;
|
|
1277
|
+
}
|
|
1278
|
+
else {
|
|
1279
|
+
delete errors[key];
|
|
1280
|
+
}
|
|
1281
|
+
control.setErrors(Object.keys(errors).length ? errors : null);
|
|
1282
|
+
}
|
|
1283
|
+
updateSchemaSourceState() {
|
|
1284
|
+
const rawArray = this.arrayValue();
|
|
1285
|
+
const hasRef = typeof rawArray.itemSchemaRef === 'string' && rawArray.itemSchemaRef.trim().length > 0;
|
|
1286
|
+
const hasFields = this.formArray.length > 0;
|
|
1287
|
+
if (this.schemaSourceState === 'imported' &&
|
|
1288
|
+
hasFields &&
|
|
1289
|
+
this.importedFieldsFingerprint === this.fieldsFingerprint()) {
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
if (this.schemaSourceState === 'imported') {
|
|
1293
|
+
this.importedFieldsFingerprint = null;
|
|
1294
|
+
}
|
|
1295
|
+
this.schemaSourceState = hasRef && hasFields ? 'override-local' : hasRef ? 'ref-only' : 'local-only';
|
|
1296
|
+
}
|
|
1297
|
+
arrayValue() {
|
|
1298
|
+
const value = this.arrayControl?.value;
|
|
1299
|
+
return value && typeof value === 'object' ? value : {};
|
|
1300
|
+
}
|
|
1301
|
+
referencedBy(fieldName) {
|
|
1302
|
+
const name = String(fieldName || '').trim();
|
|
1303
|
+
if (!name)
|
|
1304
|
+
return [];
|
|
1305
|
+
const array = this.arrayValue();
|
|
1306
|
+
const references = [];
|
|
1307
|
+
if (array.itemIdentityField === name)
|
|
1308
|
+
references.push('itemIdentityField');
|
|
1309
|
+
if (this.uniqueByFields(array.collectionValidation?.uniqueBy).includes(name)) {
|
|
1310
|
+
references.push('collectionValidation.uniqueBy');
|
|
1311
|
+
}
|
|
1312
|
+
if (array.collectionValidation?.exactlyOne?.field === name) {
|
|
1313
|
+
references.push('collectionValidation.exactlyOne.field');
|
|
1314
|
+
}
|
|
1315
|
+
if (array.collectionValidation?.atLeastOne?.field === name) {
|
|
1316
|
+
references.push('collectionValidation.atLeastOne.field');
|
|
1317
|
+
}
|
|
1318
|
+
if (typeof array.itemTitleTemplate === 'string' && this.templateReferencesField(array.itemTitleTemplate, name)) {
|
|
1319
|
+
references.push('itemTitleTemplate');
|
|
1320
|
+
}
|
|
1321
|
+
return references;
|
|
1322
|
+
}
|
|
1323
|
+
renameReferences(previous, next) {
|
|
1324
|
+
if (!this.arrayControl || !previous || !next)
|
|
1325
|
+
return;
|
|
1326
|
+
const array = { ...this.arrayValue() };
|
|
1327
|
+
if (array.itemIdentityField === previous)
|
|
1328
|
+
array.itemIdentityField = next;
|
|
1329
|
+
const collectionValidation = array.collectionValidation && typeof array.collectionValidation === 'object'
|
|
1330
|
+
? { ...array.collectionValidation }
|
|
1331
|
+
: undefined;
|
|
1332
|
+
if (collectionValidation) {
|
|
1333
|
+
if (Array.isArray(collectionValidation.uniqueBy)) {
|
|
1334
|
+
collectionValidation.uniqueBy = collectionValidation.uniqueBy.map((entry) => entry === previous ? next : entry);
|
|
1335
|
+
}
|
|
1336
|
+
else if (typeof collectionValidation.uniqueBy === 'string') {
|
|
1337
|
+
collectionValidation.uniqueBy = this.replaceUniqueByField(collectionValidation.uniqueBy, previous, next);
|
|
1338
|
+
}
|
|
1339
|
+
if (collectionValidation.exactlyOne?.field === previous) {
|
|
1340
|
+
collectionValidation.exactlyOne = { ...collectionValidation.exactlyOne, field: next };
|
|
1341
|
+
}
|
|
1342
|
+
if (collectionValidation.atLeastOne?.field === previous) {
|
|
1343
|
+
collectionValidation.atLeastOne = { ...collectionValidation.atLeastOne, field: next };
|
|
1344
|
+
}
|
|
1345
|
+
array.collectionValidation = collectionValidation;
|
|
1346
|
+
}
|
|
1347
|
+
if (typeof array.itemTitleTemplate === 'string') {
|
|
1348
|
+
array.itemTitleTemplate = array.itemTitleTemplate.replace(new RegExp(`{{\\s*${this.escapeRegExp(previous)}\\s*}}`, 'g'), `{{${next}}}`);
|
|
1349
|
+
}
|
|
1350
|
+
this.arrayControl.patchValue(array);
|
|
1351
|
+
this.arrayControl.markAsDirty();
|
|
1352
|
+
}
|
|
1353
|
+
uniqueByFields(value) {
|
|
1354
|
+
if (Array.isArray(value)) {
|
|
1355
|
+
return value.map((entry) => String(entry || '').trim()).filter(Boolean);
|
|
1356
|
+
}
|
|
1357
|
+
if (typeof value !== 'string') {
|
|
1358
|
+
return [];
|
|
1359
|
+
}
|
|
1360
|
+
const trimmed = value.trim();
|
|
1361
|
+
if (!trimmed) {
|
|
1362
|
+
return [];
|
|
1363
|
+
}
|
|
1364
|
+
try {
|
|
1365
|
+
const parsed = JSON.parse(trimmed);
|
|
1366
|
+
if (Array.isArray(parsed)) {
|
|
1367
|
+
return parsed.map((entry) => String(entry || '').trim()).filter(Boolean);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
catch { }
|
|
1371
|
+
return trimmed
|
|
1372
|
+
.split(/\r?\n|,/)
|
|
1373
|
+
.map((entry) => entry.trim())
|
|
1374
|
+
.filter(Boolean);
|
|
1375
|
+
}
|
|
1376
|
+
replaceUniqueByField(value, previous, next) {
|
|
1377
|
+
const trimmed = value.trim();
|
|
1378
|
+
if (!trimmed) {
|
|
1379
|
+
return value;
|
|
1380
|
+
}
|
|
1381
|
+
try {
|
|
1382
|
+
const parsed = JSON.parse(trimmed);
|
|
1383
|
+
if (Array.isArray(parsed)) {
|
|
1384
|
+
return JSON.stringify(parsed.map((entry) => String(entry || '').trim() === previous ? next : entry));
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
catch { }
|
|
1388
|
+
return value
|
|
1389
|
+
.split(/(\r?\n|,)/)
|
|
1390
|
+
.map((entry) => entry.trim() === previous ? next : entry)
|
|
1391
|
+
.join('');
|
|
1392
|
+
}
|
|
1393
|
+
templateReferencesField(template, fieldName) {
|
|
1394
|
+
return new RegExp(`{{\\s*${this.escapeRegExp(fieldName)}\\s*}}`).test(template);
|
|
1395
|
+
}
|
|
1396
|
+
clonePlainObject(value) {
|
|
1397
|
+
return this.cloneMetadataValue(value, new WeakMap());
|
|
1398
|
+
}
|
|
1399
|
+
cloneMetadataValue(value, seen) {
|
|
1400
|
+
if (!value || typeof value !== 'object') {
|
|
1401
|
+
return value;
|
|
1402
|
+
}
|
|
1403
|
+
if (seen.has(value)) {
|
|
1404
|
+
return seen.get(value);
|
|
1405
|
+
}
|
|
1406
|
+
if (value instanceof Date) {
|
|
1407
|
+
return new Date(value.getTime());
|
|
1408
|
+
}
|
|
1409
|
+
if (value instanceof RegExp) {
|
|
1410
|
+
return new RegExp(value.source, value.flags);
|
|
1411
|
+
}
|
|
1412
|
+
if (Array.isArray(value)) {
|
|
1413
|
+
const clone = [];
|
|
1414
|
+
seen.set(value, clone);
|
|
1415
|
+
clone.push(...value.map((entry) => this.cloneMetadataValue(entry, seen)));
|
|
1416
|
+
return clone;
|
|
1417
|
+
}
|
|
1418
|
+
if (value instanceof Map) {
|
|
1419
|
+
const clone = new Map();
|
|
1420
|
+
seen.set(value, clone);
|
|
1421
|
+
value.forEach((entry, key) => {
|
|
1422
|
+
clone.set(this.cloneMetadataValue(key, seen), this.cloneMetadataValue(entry, seen));
|
|
1423
|
+
});
|
|
1424
|
+
return clone;
|
|
1425
|
+
}
|
|
1426
|
+
if (value instanceof Set) {
|
|
1427
|
+
const clone = new Set();
|
|
1428
|
+
seen.set(value, clone);
|
|
1429
|
+
value.forEach((entry) => clone.add(this.cloneMetadataValue(entry, seen)));
|
|
1430
|
+
return clone;
|
|
1431
|
+
}
|
|
1432
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1433
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
1434
|
+
return value;
|
|
1435
|
+
}
|
|
1436
|
+
const clone = {};
|
|
1437
|
+
seen.set(value, clone);
|
|
1438
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
1439
|
+
clone[key] = this.cloneMetadataValue(entry, seen);
|
|
1440
|
+
}
|
|
1441
|
+
return clone;
|
|
1442
|
+
}
|
|
1443
|
+
fieldsFingerprint() {
|
|
1444
|
+
return JSON.stringify(this.formArray.controls.map((row) => this.normalizeField(row.getRawValue())));
|
|
1445
|
+
}
|
|
1446
|
+
escapeRegExp(value) {
|
|
1447
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
1448
|
+
}
|
|
1449
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ArrayItemSchemaFieldsEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1450
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: ArrayItemSchemaFieldsEditorComponent, isStandalone: true, selector: "praxis-array-item-schema-fields-editor", inputs: { control: "control", arrayControl: "arrayControl" }, providers: [providePraxisMetadataEditorI18n()], usesOnChanges: true, ngImport: i0, template: `
|
|
1451
|
+
<section
|
|
1452
|
+
class="array-fields-editor"
|
|
1453
|
+
[attr.data-schema-source]="schemaSourceState"
|
|
1454
|
+
[attr.aria-label]="text('praxis.metadataEditor.arrayFields.section', 'Subcampos da cole\u00e7\u00e3o')"
|
|
1455
|
+
>
|
|
1456
|
+
<header class="array-fields-editor__header">
|
|
1457
|
+
<div>
|
|
1458
|
+
<h3>{{ text('praxis.metadataEditor.arrayFields.title', 'Subcampos') }}</h3>
|
|
1459
|
+
<p>
|
|
1460
|
+
{{ schemaStateDescription() }}
|
|
1461
|
+
</p>
|
|
1462
|
+
</div>
|
|
1463
|
+
<button type="button" class="array-fields-editor__primary" (click)="addField()">
|
|
1464
|
+
{{ text('praxis.metadataEditor.arrayFields.add', 'Adicionar subcampo') }}
|
|
1465
|
+
</button>
|
|
1466
|
+
</header>
|
|
1467
|
+
|
|
1468
|
+
<div
|
|
1469
|
+
class="array-fields-editor__warning"
|
|
1470
|
+
*ngIf="duplicateNames.length"
|
|
1471
|
+
role="alert"
|
|
1472
|
+
>
|
|
1473
|
+
{{
|
|
1474
|
+
text(
|
|
1475
|
+
'praxis.metadataEditor.arrayFields.duplicateNames',
|
|
1476
|
+
undefined,
|
|
1477
|
+
'Existem nomes duplicados: {{names}}',
|
|
1478
|
+
{ names: duplicateNames.join(', ') }
|
|
1479
|
+
)
|
|
1480
|
+
}}
|
|
1481
|
+
</div>
|
|
1482
|
+
|
|
1483
|
+
<div
|
|
1484
|
+
class="array-fields-editor__empty"
|
|
1485
|
+
*ngIf="formArray.length === 0"
|
|
1486
|
+
>
|
|
1487
|
+
{{ text('praxis.metadataEditor.arrayFields.empty', 'Nenhum subcampo local configurado.') }}
|
|
1488
|
+
</div>
|
|
1489
|
+
|
|
1490
|
+
<div class="array-fields-editor__list" [formGroup]="form">
|
|
1491
|
+
<div
|
|
1492
|
+
class="array-fields-editor__row"
|
|
1493
|
+
*ngFor="let row of rows; let i = index"
|
|
1494
|
+
[formGroup]="row"
|
|
1495
|
+
>
|
|
1496
|
+
<label>
|
|
1497
|
+
<span>{{ text('praxis.metadataEditor.arrayFields.name', 'Nome') }}</span>
|
|
1498
|
+
<input
|
|
1499
|
+
type="text"
|
|
1500
|
+
formControlName="name"
|
|
1501
|
+
(blur)="onNameBlur(i)"
|
|
1502
|
+
[attr.aria-invalid]="isNameInvalid(i) ? 'true' : null"
|
|
1503
|
+
[attr.aria-describedby]="nameErrorDescribedBy(i)"
|
|
1504
|
+
/>
|
|
1505
|
+
<span
|
|
1506
|
+
class="array-fields-editor__field-error"
|
|
1507
|
+
*ngIf="nameErrorMessage(i) as nameError"
|
|
1508
|
+
[id]="nameErrorId(i)"
|
|
1509
|
+
>
|
|
1510
|
+
{{ nameError }}
|
|
1511
|
+
</span>
|
|
1512
|
+
</label>
|
|
1513
|
+
|
|
1514
|
+
<label>
|
|
1515
|
+
<span>{{ text('praxis.metadataEditor.arrayFields.label', 'Label') }}</span>
|
|
1516
|
+
<input type="text" formControlName="label" />
|
|
1517
|
+
</label>
|
|
1518
|
+
|
|
1519
|
+
<label>
|
|
1520
|
+
<span>{{ text('praxis.metadataEditor.arrayFields.controlType', 'Controle') }}</span>
|
|
1521
|
+
<select formControlName="controlType">
|
|
1522
|
+
<option *ngFor="let option of controlTypeOptions" [value]="option">
|
|
1523
|
+
{{ controlTypeLabel(option) }}
|
|
1524
|
+
</option>
|
|
1525
|
+
</select>
|
|
1526
|
+
</label>
|
|
1527
|
+
|
|
1528
|
+
<label class="array-fields-editor__check">
|
|
1529
|
+
<input type="checkbox" formControlName="required" />
|
|
1530
|
+
<span>{{ text('praxis.metadataEditor.arrayFields.required', 'Obrigat\u00f3rio') }}</span>
|
|
1531
|
+
</label>
|
|
1532
|
+
|
|
1533
|
+
<div class="array-fields-editor__actions">
|
|
1534
|
+
<button type="button" (click)="moveField(i, -1)" [disabled]="i === 0">
|
|
1535
|
+
{{ text('praxis.metadataEditor.arrayFields.moveUp', 'Subir') }}
|
|
1536
|
+
</button>
|
|
1537
|
+
<button type="button" (click)="moveField(i, 1)" [disabled]="i === formArray.length - 1">
|
|
1538
|
+
{{ text('praxis.metadataEditor.arrayFields.moveDown', 'Descer') }}
|
|
1539
|
+
</button>
|
|
1540
|
+
<button type="button" (click)="duplicateField(i)">
|
|
1541
|
+
{{ text('praxis.metadataEditor.arrayFields.duplicate', 'Duplicar') }}
|
|
1542
|
+
</button>
|
|
1543
|
+
<button
|
|
1544
|
+
type="button"
|
|
1545
|
+
(click)="removeField(i)"
|
|
1546
|
+
[disabled]="isReferenced(row.get('name')?.value)"
|
|
1547
|
+
[title]="removeTitle(row.get('name')?.value)"
|
|
1548
|
+
>
|
|
1549
|
+
{{ text('praxis.metadataEditor.arrayFields.remove', 'Remover') }}
|
|
1550
|
+
</button>
|
|
1551
|
+
</div>
|
|
1552
|
+
</div>
|
|
1553
|
+
</div>
|
|
1554
|
+
</section>
|
|
1555
|
+
`, isInline: true, styles: [":host{display:block}.array-fields-editor{display:block;margin:10px 0 2px;padding:12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface)}.array-fields-editor__header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}.array-fields-editor__header h3{margin:0 0 4px;font-size:1rem;line-height:1.25}.array-fields-editor__header p,.array-fields-editor__empty{margin:0;color:var(--md-sys-color-on-surface-variant);font-size:.875rem;line-height:1.35}.array-fields-editor__primary,.array-fields-editor button{border:1px solid var(--md-sys-color-outline-variant);border-radius:6px;background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer;min-height:32px;padding:5px 10px}.array-fields-editor__primary{border-color:var(--md-sys-color-primary);color:var(--md-sys-color-primary);font-weight:600;white-space:nowrap}.array-fields-editor button:disabled{cursor:not-allowed;opacity:.55}.array-fields-editor__warning{margin:8px 0 12px;padding:8px 10px;border:1px solid var(--md-sys-color-error);border-radius:6px;color:var(--md-sys-color-error);background:var(--md-sys-color-error-container)}.array-fields-editor__list{display:grid;gap:10px}.array-fields-editor__row{display:grid;grid-template-columns:minmax(140px,1fr) minmax(160px,1.2fr) minmax(140px,.9fr) auto;gap:10px;align-items:end;padding:10px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface)}.array-fields-editor label{display:grid;gap:4px;font-size:.8rem;font-weight:600}.array-fields-editor input[type=text],.array-fields-editor select{box-sizing:border-box;width:100%;min-height:34px;border:1px solid var(--md-sys-color-outline-variant);border-radius:6px;background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);padding:5px 8px}.array-fields-editor input[aria-invalid=true]{border-color:var(--md-sys-color-error)}.array-fields-editor__field-error{color:var(--md-sys-color-error);font-size:.75rem;font-weight:500;line-height:1.3}.array-fields-editor__check{display:flex!important;align-items:center;gap:6px;min-height:34px}.array-fields-editor__actions{grid-column:1 / -1;display:flex;flex-wrap:wrap;gap:6px}@media(max-width:720px){.array-fields-editor__header,.array-fields-editor__row{display:grid;grid-template-columns:1fr}.array-fields-editor__primary{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
|
|
1556
|
+
}
|
|
1557
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ArrayItemSchemaFieldsEditorComponent, decorators: [{
|
|
1558
|
+
type: Component,
|
|
1559
|
+
args: [{ selector: 'praxis-array-item-schema-fields-editor', standalone: true, imports: [CommonModule, ReactiveFormsModule], providers: [providePraxisMetadataEditorI18n()], template: `
|
|
1560
|
+
<section
|
|
1561
|
+
class="array-fields-editor"
|
|
1562
|
+
[attr.data-schema-source]="schemaSourceState"
|
|
1563
|
+
[attr.aria-label]="text('praxis.metadataEditor.arrayFields.section', 'Subcampos da cole\u00e7\u00e3o')"
|
|
1564
|
+
>
|
|
1565
|
+
<header class="array-fields-editor__header">
|
|
1566
|
+
<div>
|
|
1567
|
+
<h3>{{ text('praxis.metadataEditor.arrayFields.title', 'Subcampos') }}</h3>
|
|
1568
|
+
<p>
|
|
1569
|
+
{{ schemaStateDescription() }}
|
|
1570
|
+
</p>
|
|
1571
|
+
</div>
|
|
1572
|
+
<button type="button" class="array-fields-editor__primary" (click)="addField()">
|
|
1573
|
+
{{ text('praxis.metadataEditor.arrayFields.add', 'Adicionar subcampo') }}
|
|
1574
|
+
</button>
|
|
1575
|
+
</header>
|
|
1576
|
+
|
|
1577
|
+
<div
|
|
1578
|
+
class="array-fields-editor__warning"
|
|
1579
|
+
*ngIf="duplicateNames.length"
|
|
1580
|
+
role="alert"
|
|
1581
|
+
>
|
|
1582
|
+
{{
|
|
1583
|
+
text(
|
|
1584
|
+
'praxis.metadataEditor.arrayFields.duplicateNames',
|
|
1585
|
+
undefined,
|
|
1586
|
+
'Existem nomes duplicados: {{names}}',
|
|
1587
|
+
{ names: duplicateNames.join(', ') }
|
|
1588
|
+
)
|
|
1589
|
+
}}
|
|
1590
|
+
</div>
|
|
1591
|
+
|
|
1592
|
+
<div
|
|
1593
|
+
class="array-fields-editor__empty"
|
|
1594
|
+
*ngIf="formArray.length === 0"
|
|
1595
|
+
>
|
|
1596
|
+
{{ text('praxis.metadataEditor.arrayFields.empty', 'Nenhum subcampo local configurado.') }}
|
|
1597
|
+
</div>
|
|
1598
|
+
|
|
1599
|
+
<div class="array-fields-editor__list" [formGroup]="form">
|
|
1600
|
+
<div
|
|
1601
|
+
class="array-fields-editor__row"
|
|
1602
|
+
*ngFor="let row of rows; let i = index"
|
|
1603
|
+
[formGroup]="row"
|
|
1604
|
+
>
|
|
1605
|
+
<label>
|
|
1606
|
+
<span>{{ text('praxis.metadataEditor.arrayFields.name', 'Nome') }}</span>
|
|
1607
|
+
<input
|
|
1608
|
+
type="text"
|
|
1609
|
+
formControlName="name"
|
|
1610
|
+
(blur)="onNameBlur(i)"
|
|
1611
|
+
[attr.aria-invalid]="isNameInvalid(i) ? 'true' : null"
|
|
1612
|
+
[attr.aria-describedby]="nameErrorDescribedBy(i)"
|
|
1613
|
+
/>
|
|
1614
|
+
<span
|
|
1615
|
+
class="array-fields-editor__field-error"
|
|
1616
|
+
*ngIf="nameErrorMessage(i) as nameError"
|
|
1617
|
+
[id]="nameErrorId(i)"
|
|
1618
|
+
>
|
|
1619
|
+
{{ nameError }}
|
|
1620
|
+
</span>
|
|
1621
|
+
</label>
|
|
1622
|
+
|
|
1623
|
+
<label>
|
|
1624
|
+
<span>{{ text('praxis.metadataEditor.arrayFields.label', 'Label') }}</span>
|
|
1625
|
+
<input type="text" formControlName="label" />
|
|
1626
|
+
</label>
|
|
1627
|
+
|
|
1628
|
+
<label>
|
|
1629
|
+
<span>{{ text('praxis.metadataEditor.arrayFields.controlType', 'Controle') }}</span>
|
|
1630
|
+
<select formControlName="controlType">
|
|
1631
|
+
<option *ngFor="let option of controlTypeOptions" [value]="option">
|
|
1632
|
+
{{ controlTypeLabel(option) }}
|
|
1633
|
+
</option>
|
|
1634
|
+
</select>
|
|
1635
|
+
</label>
|
|
1636
|
+
|
|
1637
|
+
<label class="array-fields-editor__check">
|
|
1638
|
+
<input type="checkbox" formControlName="required" />
|
|
1639
|
+
<span>{{ text('praxis.metadataEditor.arrayFields.required', 'Obrigat\u00f3rio') }}</span>
|
|
1640
|
+
</label>
|
|
1641
|
+
|
|
1642
|
+
<div class="array-fields-editor__actions">
|
|
1643
|
+
<button type="button" (click)="moveField(i, -1)" [disabled]="i === 0">
|
|
1644
|
+
{{ text('praxis.metadataEditor.arrayFields.moveUp', 'Subir') }}
|
|
1645
|
+
</button>
|
|
1646
|
+
<button type="button" (click)="moveField(i, 1)" [disabled]="i === formArray.length - 1">
|
|
1647
|
+
{{ text('praxis.metadataEditor.arrayFields.moveDown', 'Descer') }}
|
|
1648
|
+
</button>
|
|
1649
|
+
<button type="button" (click)="duplicateField(i)">
|
|
1650
|
+
{{ text('praxis.metadataEditor.arrayFields.duplicate', 'Duplicar') }}
|
|
1651
|
+
</button>
|
|
1652
|
+
<button
|
|
1653
|
+
type="button"
|
|
1654
|
+
(click)="removeField(i)"
|
|
1655
|
+
[disabled]="isReferenced(row.get('name')?.value)"
|
|
1656
|
+
[title]="removeTitle(row.get('name')?.value)"
|
|
1657
|
+
>
|
|
1658
|
+
{{ text('praxis.metadataEditor.arrayFields.remove', 'Remover') }}
|
|
1659
|
+
</button>
|
|
1660
|
+
</div>
|
|
1661
|
+
</div>
|
|
1662
|
+
</div>
|
|
1663
|
+
</section>
|
|
1664
|
+
`, styles: [":host{display:block}.array-fields-editor{display:block;margin:10px 0 2px;padding:12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface)}.array-fields-editor__header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}.array-fields-editor__header h3{margin:0 0 4px;font-size:1rem;line-height:1.25}.array-fields-editor__header p,.array-fields-editor__empty{margin:0;color:var(--md-sys-color-on-surface-variant);font-size:.875rem;line-height:1.35}.array-fields-editor__primary,.array-fields-editor button{border:1px solid var(--md-sys-color-outline-variant);border-radius:6px;background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer;min-height:32px;padding:5px 10px}.array-fields-editor__primary{border-color:var(--md-sys-color-primary);color:var(--md-sys-color-primary);font-weight:600;white-space:nowrap}.array-fields-editor button:disabled{cursor:not-allowed;opacity:.55}.array-fields-editor__warning{margin:8px 0 12px;padding:8px 10px;border:1px solid var(--md-sys-color-error);border-radius:6px;color:var(--md-sys-color-error);background:var(--md-sys-color-error-container)}.array-fields-editor__list{display:grid;gap:10px}.array-fields-editor__row{display:grid;grid-template-columns:minmax(140px,1fr) minmax(160px,1.2fr) minmax(140px,.9fr) auto;gap:10px;align-items:end;padding:10px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface)}.array-fields-editor label{display:grid;gap:4px;font-size:.8rem;font-weight:600}.array-fields-editor input[type=text],.array-fields-editor select{box-sizing:border-box;width:100%;min-height:34px;border:1px solid var(--md-sys-color-outline-variant);border-radius:6px;background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);padding:5px 8px}.array-fields-editor input[aria-invalid=true]{border-color:var(--md-sys-color-error)}.array-fields-editor__field-error{color:var(--md-sys-color-error);font-size:.75rem;font-weight:500;line-height:1.3}.array-fields-editor__check{display:flex!important;align-items:center;gap:6px;min-height:34px}.array-fields-editor__actions{grid-column:1 / -1;display:flex;flex-wrap:wrap;gap:6px}@media(max-width:720px){.array-fields-editor__header,.array-fields-editor__row{display:grid;grid-template-columns:1fr}.array-fields-editor__primary{width:100%}}\n"] }]
|
|
1665
|
+
}], propDecorators: { control: [{
|
|
1666
|
+
type: Input
|
|
1667
|
+
}], arrayControl: [{
|
|
1668
|
+
type: Input
|
|
1669
|
+
}] } });
|
|
1670
|
+
|
|
846
1671
|
const inputProperties = [
|
|
847
1672
|
// Geral
|
|
848
1673
|
{ name: 'label', label: 'Label', editorType: 'text', placeholder: 'Rótulo do campo', group: 'Geral' },
|
|
@@ -1054,6 +1879,61 @@ const inputProperties = [
|
|
|
1054
1879
|
{ name: 'defaultValue', label: 'Valor padrão', editorType: 'text', group: 'Geral' },
|
|
1055
1880
|
];
|
|
1056
1881
|
|
|
1882
|
+
const arrayProperties = [
|
|
1883
|
+
{ name: 'label', label: 'Label', editorType: 'text', group: 'Geral' },
|
|
1884
|
+
{ name: 'hint', label: 'Hint', editorType: 'text', group: 'Geral' },
|
|
1885
|
+
{ name: 'defaultValue', label: 'Valor inicial (JSON array)', editorType: 'textarea', group: 'Geral' },
|
|
1886
|
+
{
|
|
1887
|
+
name: 'array.itemType',
|
|
1888
|
+
label: 'Tipo do item',
|
|
1889
|
+
editorType: 'select',
|
|
1890
|
+
group: 'Colecao',
|
|
1891
|
+
defaultValue: 'object',
|
|
1892
|
+
options: [
|
|
1893
|
+
{ value: 'object', text: 'Objeto' },
|
|
1894
|
+
],
|
|
1895
|
+
},
|
|
1896
|
+
{
|
|
1897
|
+
name: 'array.mode',
|
|
1898
|
+
label: 'Modo',
|
|
1899
|
+
editorType: 'select',
|
|
1900
|
+
group: 'Colecao',
|
|
1901
|
+
defaultValue: 'cards',
|
|
1902
|
+
options: [
|
|
1903
|
+
{ value: 'cards', text: 'Cards' },
|
|
1904
|
+
],
|
|
1905
|
+
},
|
|
1906
|
+
{ name: 'array.itemSchemaRef', label: 'Schema do item ($ref)', editorType: 'text', group: 'Colecao' },
|
|
1907
|
+
{ name: 'array.itemIdentityField', label: 'Campo de identidade', editorType: 'text', group: 'Colecao' },
|
|
1908
|
+
{ name: 'array.addLabel', label: 'Texto do botao adicionar', editorType: 'text', group: 'Colecao' },
|
|
1909
|
+
{ name: 'array.emptyState', label: 'Texto vazio', editorType: 'text', group: 'Colecao' },
|
|
1910
|
+
{ name: 'array.itemTitleTemplate', label: 'Template do titulo do item', editorType: 'text', group: 'Colecao', hint: 'Ex.: {{nome}} - {{cargo}}' },
|
|
1911
|
+
{ name: 'array.minItems', label: 'Minimo de itens', editorType: 'number', group: 'Validacao' },
|
|
1912
|
+
{ name: 'array.maxItems', label: 'Maximo de itens', editorType: 'number', group: 'Validacao' },
|
|
1913
|
+
{
|
|
1914
|
+
name: 'array.deleteMode',
|
|
1915
|
+
label: 'Modo de exclusao',
|
|
1916
|
+
editorType: 'select',
|
|
1917
|
+
group: 'Operacoes',
|
|
1918
|
+
defaultValue: 'removeFromPayload',
|
|
1919
|
+
options: [
|
|
1920
|
+
{ value: 'removeFromPayload', text: 'Remover do payload' },
|
|
1921
|
+
],
|
|
1922
|
+
},
|
|
1923
|
+
{ name: 'array.operations.add', label: 'Permitir adicionar', editorType: 'checkbox', group: 'Operacoes', defaultValue: true },
|
|
1924
|
+
{ name: 'array.operations.edit', label: 'Permitir editar', editorType: 'checkbox', group: 'Operacoes', defaultValue: true },
|
|
1925
|
+
{ name: 'array.operations.remove', label: 'Permitir remover', editorType: 'checkbox', group: 'Operacoes', defaultValue: true },
|
|
1926
|
+
{ name: 'array.collectionValidation.uniqueBy', label: 'Unico por campos', editorType: 'textarea', group: 'Validacao', hint: 'JSON array ou uma linha por campo. Ex.: ["email"]' },
|
|
1927
|
+
{ name: 'array.collectionValidation.exactlyOne.field', label: 'Exatamente um: campo', editorType: 'text', group: 'Validacao' },
|
|
1928
|
+
{ name: 'array.collectionValidation.exactlyOne.value', label: 'Exatamente um: valor tipado', editorType: 'text', group: 'Validacao', hint: 'Use true/false, numero ou texto. Ex.: true, 1, PRINCIPAL.' },
|
|
1929
|
+
{ name: 'array.collectionValidation.exactlyOne.message', label: 'Mensagem: exatamente um', editorType: 'text', group: 'Validacao' },
|
|
1930
|
+
{ name: 'array.collectionValidation.atLeastOne.field', label: 'Pelo menos um: campo', editorType: 'text', group: 'Validacao' },
|
|
1931
|
+
{ name: 'array.collectionValidation.atLeastOne.value', label: 'Pelo menos um: valor tipado', editorType: 'text', group: 'Validacao', hint: 'Use true/false, numero ou texto. Ex.: true, 1, ACTIVE.' },
|
|
1932
|
+
{ name: 'array.collectionValidation.atLeastOne.message', label: 'Mensagem: pelo menos um', editorType: 'text', group: 'Validacao' },
|
|
1933
|
+
{ name: 'array.itemSchema.fields', label: 'Subcampos', editorType: 'field-list', group: 'Subcampos', hint: 'Lista FieldMetadata[] usada quando nao houver itemSchemaRef resolvido.' },
|
|
1934
|
+
{ name: 'required', label: 'Obrigatorio', editorType: 'checkbox', group: 'Validacao' },
|
|
1935
|
+
];
|
|
1936
|
+
|
|
1057
1937
|
// Editor de metadados para componentes SELECT-like
|
|
1058
1938
|
// Paridade com INPUT: grupos, linhas e propriedades canônicas
|
|
1059
1939
|
const selectProperties = [
|
|
@@ -1131,88 +2011,712 @@ const selectProperties = [
|
|
|
1131
2011
|
{ value: 'manual', text: 'Manual (Connections disparam)' },
|
|
1132
2012
|
]
|
|
1133
2013
|
},
|
|
1134
|
-
// 4) Formato/Comportamento
|
|
1135
|
-
{ name: 'selectAll', label: 'Selecionar todos', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.inline1', inline: true, hint: 'Exibe opção para selecionar tudo (somente multiple).' },
|
|
1136
|
-
{ name: 'searchable', label: 'Buscável', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.inline1', inline: true, hint: 'Mostra campo de busca; em remoto, filtra na API.' },
|
|
1137
|
-
{ name: 'searchPlaceholder', label: 'Placeholder da busca', editorType: 'text', group: 'Formato/Comportamento', hint: 'Texto do campo de busca no popover/lista.' },
|
|
1138
|
-
{ name: 'maxSelections', label: 'Máximo de seleções', editorType: 'number', group: 'Formato/Comportamento', hint: 'Limite de itens quando multiple=true.' },
|
|
1139
|
-
{ name: 'readonly', label: 'Somente leitura', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.inline2', inline: true, hint: 'Mantém o valor visível sem permitir interação.' },
|
|
1140
|
-
{ name: 'disabled', label: 'Desabilitado', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.inline2', inline: true, hint: 'Desativa interação, foco e ações do trigger.' },
|
|
1141
|
-
// 5) Validação
|
|
1142
|
-
{ name: 'required', label: 'Obrigatório', editorType: 'checkbox', group: 'Validação' },
|
|
1143
|
-
{ name: 'validators.requiredMessage', label: 'Mensagem: obrigatório', editorType: 'text', group: 'Validação' },
|
|
1144
|
-
// 6) Ações (Clear)
|
|
1145
|
-
{ name: 'clearButton.enabled', label: 'Clear: habilitar', editorType: 'checkbox', group: 'Ações', row: 'clear.inline1', inline: true, hint: 'Mostra botão para limpar seleção.' },
|
|
1146
|
-
{ name: 'clearButton.icon', label: 'Clear: ícone', editorType: 'text', group: 'Ações', row: 'clear.inline2', hint: "Material Symbols (ex.: mi:clear). Use o seletor ao lado para buscar." },
|
|
1147
|
-
{ name: 'clearButton.iconColor', label: 'Clear: cor do ícone', editorType: 'color', group: 'Ações', row: 'clear.inline2', hint: 'Tema (primary/accent/warn) ou cor CSS (ex.: #496ddb, rgb(73,109,219), red).' },
|
|
1148
|
-
{ name: 'clearButton.tooltip', label: 'Clear: tooltip', editorType: 'text', group: 'Ações', row: 'clear.inline2' },
|
|
1149
|
-
{ name: 'clearButton.ariaLabel', label: 'Clear: aria-label', editorType: 'text', group: 'Ações', row: 'clear.inline2', hint: 'Obrigatório quando não houver texto visível.' },
|
|
1150
|
-
{ name: 'clearButton.showOnlyWhenFilled', label: 'Clear: só quando preenchido', editorType: 'checkbox', group: 'Ações', row: 'clear.inline1', inline: true, hint: 'Exibe o botão apenas quando há valor.' },
|
|
1151
|
-
// 7) Aparência
|
|
2014
|
+
// 4) Formato/Comportamento
|
|
2015
|
+
{ name: 'selectAll', label: 'Selecionar todos', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.inline1', inline: true, hint: 'Exibe opção para selecionar tudo (somente multiple).' },
|
|
2016
|
+
{ name: 'searchable', label: 'Buscável', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.inline1', inline: true, hint: 'Mostra campo de busca; em remoto, filtra na API.' },
|
|
2017
|
+
{ name: 'searchPlaceholder', label: 'Placeholder da busca', editorType: 'text', group: 'Formato/Comportamento', hint: 'Texto do campo de busca no popover/lista.' },
|
|
2018
|
+
{ name: 'maxSelections', label: 'Máximo de seleções', editorType: 'number', group: 'Formato/Comportamento', hint: 'Limite de itens quando multiple=true.' },
|
|
2019
|
+
{ name: 'readonly', label: 'Somente leitura', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.inline2', inline: true, hint: 'Mantém o valor visível sem permitir interação.' },
|
|
2020
|
+
{ name: 'disabled', label: 'Desabilitado', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.inline2', inline: true, hint: 'Desativa interação, foco e ações do trigger.' },
|
|
2021
|
+
// 5) Validação
|
|
2022
|
+
{ name: 'required', label: 'Obrigatório', editorType: 'checkbox', group: 'Validação' },
|
|
2023
|
+
{ name: 'validators.requiredMessage', label: 'Mensagem: obrigatório', editorType: 'text', group: 'Validação' },
|
|
2024
|
+
// 6) Ações (Clear)
|
|
2025
|
+
{ name: 'clearButton.enabled', label: 'Clear: habilitar', editorType: 'checkbox', group: 'Ações', row: 'clear.inline1', inline: true, hint: 'Mostra botão para limpar seleção.' },
|
|
2026
|
+
{ name: 'clearButton.icon', label: 'Clear: ícone', editorType: 'text', group: 'Ações', row: 'clear.inline2', hint: "Material Symbols (ex.: mi:clear). Use o seletor ao lado para buscar." },
|
|
2027
|
+
{ name: 'clearButton.iconColor', label: 'Clear: cor do ícone', editorType: 'color', group: 'Ações', row: 'clear.inline2', hint: 'Tema (primary/accent/warn) ou cor CSS (ex.: #496ddb, rgb(73,109,219), red).' },
|
|
2028
|
+
{ name: 'clearButton.tooltip', label: 'Clear: tooltip', editorType: 'text', group: 'Ações', row: 'clear.inline2' },
|
|
2029
|
+
{ name: 'clearButton.ariaLabel', label: 'Clear: aria-label', editorType: 'text', group: 'Ações', row: 'clear.inline2', hint: 'Obrigatório quando não houver texto visível.' },
|
|
2030
|
+
{ name: 'clearButton.showOnlyWhenFilled', label: 'Clear: só quando preenchido', editorType: 'checkbox', group: 'Ações', row: 'clear.inline1', inline: true, hint: 'Exibe o botão apenas quando há valor.' },
|
|
2031
|
+
// 7) Aparência
|
|
2032
|
+
{
|
|
2033
|
+
name: 'materialDesign.appearance',
|
|
2034
|
+
label: 'Aparência',
|
|
2035
|
+
editorType: 'select',
|
|
2036
|
+
group: 'Aparência',
|
|
2037
|
+
options: [
|
|
2038
|
+
{ value: 'fill', text: 'Fill' },
|
|
2039
|
+
{ value: 'outline', text: 'Outline' },
|
|
2040
|
+
],
|
|
2041
|
+
},
|
|
2042
|
+
{
|
|
2043
|
+
name: 'materialDesign.color',
|
|
2044
|
+
label: 'Cor do tema',
|
|
2045
|
+
editorType: 'select',
|
|
2046
|
+
group: 'Aparência',
|
|
2047
|
+
options: [
|
|
2048
|
+
{ value: 'primary', text: 'Primária' },
|
|
2049
|
+
{ value: 'accent', text: 'Acento' },
|
|
2050
|
+
{ value: 'warn', text: 'Alerta' },
|
|
2051
|
+
],
|
|
2052
|
+
},
|
|
2053
|
+
{
|
|
2054
|
+
name: 'materialDesign.floatLabel',
|
|
2055
|
+
label: 'Comportamento do label',
|
|
2056
|
+
editorType: 'select',
|
|
2057
|
+
group: 'Aparência',
|
|
2058
|
+
options: [
|
|
2059
|
+
{ value: 'auto', text: 'Auto' },
|
|
2060
|
+
{ value: 'always', text: 'Sempre' },
|
|
2061
|
+
],
|
|
2062
|
+
},
|
|
2063
|
+
{
|
|
2064
|
+
name: 'materialDesign.subscriptSizing',
|
|
2065
|
+
label: 'Subscript sizing',
|
|
2066
|
+
editorType: 'select',
|
|
2067
|
+
group: 'Aparência',
|
|
2068
|
+
options: [
|
|
2069
|
+
{ value: 'fixed', text: 'Fixo' },
|
|
2070
|
+
{ value: 'dynamic', text: 'Dinâmico' },
|
|
2071
|
+
],
|
|
2072
|
+
},
|
|
2073
|
+
{ name: 'inlineAutoSize.minWidth', label: 'Inline: largura mínima (desktop)', editorType: 'number', group: 'Aparência', row: 'appearance.inlineAutoSizeDesktop', inline: true, hint: 'Largura mínima em px para desktop.' },
|
|
2074
|
+
{ name: 'inlineAutoSize.maxWidth', label: 'Inline: largura máxima (desktop)', editorType: 'number', group: 'Aparência', row: 'appearance.inlineAutoSizeDesktop', inline: true, hint: 'Largura máxima em px para desktop.' },
|
|
2075
|
+
{ name: 'inlineAutoSize.minWidthMobile', label: 'Inline: largura mínima (mobile)', editorType: 'number', group: 'Aparência', row: 'appearance.inlineAutoSizeMobile', inline: true, hint: 'Largura mínima em px para mobile.' },
|
|
2076
|
+
{ name: 'inlineAutoSize.maxWidthMobile', label: 'Inline: largura máxima (mobile)', editorType: 'number', group: 'Aparência', row: 'appearance.inlineAutoSizeMobile', inline: true, hint: 'Largura máxima em px para mobile.' },
|
|
2077
|
+
{ name: 'materialDesign.hideRequiredMarker', label: 'Ocultar * obrigatório', editorType: 'checkbox', group: 'Aparência' },
|
|
2078
|
+
{
|
|
2079
|
+
name: 'errorStateMatcher',
|
|
2080
|
+
label: 'Estratégia de erro',
|
|
2081
|
+
editorType: 'select',
|
|
2082
|
+
group: 'Aparência',
|
|
2083
|
+
options: [
|
|
2084
|
+
{ value: 'default', text: 'Padrão' },
|
|
2085
|
+
{ value: 'showOnDirtyAndInvalid', text: 'Mostrar ao sujar e inválido' },
|
|
2086
|
+
{ value: 'showOnSubmitted', text: 'Mostrar ao enviar' },
|
|
2087
|
+
{ value: 'showImmediately', text: 'Mostrar imediatamente' },
|
|
2088
|
+
],
|
|
2089
|
+
},
|
|
2090
|
+
// 8) Acessibilidade
|
|
2091
|
+
{ name: 'ariaLabel', label: 'ARIA label', editorType: 'text', group: 'Acessibilidade', hint: 'Use quando não houver label visível.' },
|
|
2092
|
+
{ name: 'ariaDescribedby', label: 'ARIA describedby', editorType: 'text', group: 'Acessibilidade', hint: 'IDs de ajuda/erro.' },
|
|
2093
|
+
{ name: 'ariaLabelledby', label: 'ARIA labelledby', editorType: 'text', group: 'Acessibilidade', hint: 'ID de elemento que atua como label.' },
|
|
2094
|
+
{ name: 'tabIndex', label: 'TabIndex', editorType: 'number', group: 'Acessibilidade' },
|
|
2095
|
+
{ name: 'dataAttributes', label: 'Data attributes (JSON)', editorType: 'textarea', group: 'Acessibilidade', hint: '{ "testId": "select-a" }' },
|
|
2096
|
+
];
|
|
2097
|
+
|
|
2098
|
+
function cloneProperty$7(prop) {
|
|
2099
|
+
return {
|
|
2100
|
+
...prop,
|
|
2101
|
+
options: Array.isArray(prop.options)
|
|
2102
|
+
? prop.options.map((opt) => typeof opt === 'object' && opt !== null ? { ...opt } : opt)
|
|
2103
|
+
: prop.options,
|
|
2104
|
+
};
|
|
2105
|
+
}
|
|
2106
|
+
function cloneEntityLookupBaseProperty(prop) {
|
|
2107
|
+
const cloned = cloneProperty$7(prop);
|
|
2108
|
+
if (cloned.name === 'multiple') {
|
|
2109
|
+
return {
|
|
2110
|
+
...cloned,
|
|
2111
|
+
group: 'Selecao em colecao',
|
|
2112
|
+
hint: 'Ativa selecao governada de varias entidades no mesmo campo.',
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
if (cloned.name === 'maxSelections') {
|
|
2116
|
+
return {
|
|
2117
|
+
...cloned,
|
|
2118
|
+
group: 'Selecao em colecao',
|
|
2119
|
+
hint: 'Limite maximo de entidades selecionadas quando multiple=true.',
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
return cloned;
|
|
2123
|
+
}
|
|
2124
|
+
const entityLookupProperties = [
|
|
2125
|
+
...selectProperties.map((prop) => cloneEntityLookupBaseProperty(prop)),
|
|
2126
|
+
{
|
|
2127
|
+
name: 'payloadMode',
|
|
2128
|
+
label: 'Modo do payload',
|
|
2129
|
+
editorType: 'select',
|
|
2130
|
+
group: 'Selecao em colecao',
|
|
2131
|
+
options: [
|
|
2132
|
+
{ value: 'id', text: 'id' },
|
|
2133
|
+
{ value: 'entityRef', text: 'entityRef' },
|
|
2134
|
+
{ value: 'ids', text: 'ids' },
|
|
2135
|
+
{ value: 'entityRefs', text: 'entityRefs' },
|
|
2136
|
+
],
|
|
2137
|
+
hint: 'Define o shape salvo no payload. O runtime resolve combinacoes invalidas conforme multiple.',
|
|
2138
|
+
},
|
|
2139
|
+
{
|
|
2140
|
+
name: 'optionSource.key',
|
|
2141
|
+
label: 'Option source key',
|
|
2142
|
+
editorType: 'text',
|
|
2143
|
+
group: 'Fonte de entidade',
|
|
2144
|
+
required: true,
|
|
2145
|
+
hint: 'Chave publicada pelo backend para filtrar e reidratar a entidade.',
|
|
2146
|
+
},
|
|
2147
|
+
{
|
|
2148
|
+
name: 'optionSource.type',
|
|
2149
|
+
label: 'Tipo da fonte',
|
|
2150
|
+
editorType: 'select',
|
|
2151
|
+
group: 'Fonte de entidade',
|
|
2152
|
+
defaultValue: 'RESOURCE_ENTITY',
|
|
2153
|
+
options: [
|
|
2154
|
+
{ value: 'RESOURCE_ENTITY', text: 'RESOURCE_ENTITY' },
|
|
2155
|
+
{ value: 'LIGHT_LOOKUP', text: 'LIGHT_LOOKUP' },
|
|
2156
|
+
],
|
|
2157
|
+
hint: 'Use RESOURCE_ENTITY para entidades reais de dominio.',
|
|
2158
|
+
},
|
|
2159
|
+
{
|
|
2160
|
+
name: 'optionSource.entityKey',
|
|
2161
|
+
label: 'Entity key',
|
|
2162
|
+
editorType: 'text',
|
|
2163
|
+
group: 'Fonte de entidade',
|
|
2164
|
+
hint: 'Identificador semantico da entidade, por exemplo supplier, customer ou contract.',
|
|
2165
|
+
},
|
|
2166
|
+
{
|
|
2167
|
+
name: 'optionSource.resourcePath',
|
|
2168
|
+
label: 'Resource path',
|
|
2169
|
+
editorType: 'text',
|
|
2170
|
+
group: 'Fonte de entidade',
|
|
2171
|
+
hint: 'Caminho base do recurso que publica option-sources e by-ids.',
|
|
2172
|
+
},
|
|
2173
|
+
{
|
|
2174
|
+
name: 'optionSource.filterField',
|
|
2175
|
+
label: 'Campo de filtro principal',
|
|
2176
|
+
editorType: 'text',
|
|
2177
|
+
group: 'Fonte de entidade',
|
|
2178
|
+
hint: 'Campo usado por fontes derivadas quando houver filtro simples no backend.',
|
|
2179
|
+
},
|
|
2180
|
+
{
|
|
2181
|
+
name: 'optionSource.propertyPath',
|
|
2182
|
+
label: 'Property path',
|
|
2183
|
+
editorType: 'text',
|
|
2184
|
+
group: 'Fonte de entidade',
|
|
2185
|
+
hint: 'Caminho de propriedade usado por fontes derivadas ou projeções leves.',
|
|
2186
|
+
},
|
|
2187
|
+
{
|
|
2188
|
+
name: 'optionSource.valuePropertyPath',
|
|
2189
|
+
label: 'Campo de valor',
|
|
2190
|
+
editorType: 'text',
|
|
2191
|
+
group: 'Fonte de entidade',
|
|
2192
|
+
defaultValue: 'id',
|
|
2193
|
+
hint: 'Campo canonico salvo no payload do formulario.',
|
|
2194
|
+
},
|
|
2195
|
+
{
|
|
2196
|
+
name: 'optionSource.labelPropertyPath',
|
|
2197
|
+
label: 'Campo de label',
|
|
2198
|
+
editorType: 'text',
|
|
2199
|
+
group: 'Fonte de entidade',
|
|
2200
|
+
defaultValue: 'label',
|
|
2201
|
+
hint: 'Campo principal exibido para o usuario.',
|
|
2202
|
+
},
|
|
2203
|
+
{
|
|
2204
|
+
name: 'optionSource.codePropertyPath',
|
|
2205
|
+
label: 'Campo de codigo',
|
|
2206
|
+
editorType: 'text',
|
|
2207
|
+
group: 'Exibicao de entidade',
|
|
2208
|
+
hint: 'Codigo curto exibido antes do label, por exemplo FOR-8742.',
|
|
2209
|
+
},
|
|
2210
|
+
{
|
|
2211
|
+
name: 'optionSource.descriptionPropertyPaths',
|
|
2212
|
+
label: 'Campos de descricao',
|
|
2213
|
+
editorType: 'textarea',
|
|
2214
|
+
group: 'Exibicao de entidade',
|
|
2215
|
+
hint: 'JSON array ou uma linha por campo. Ex.: ["documentNumber", "city", "state"].',
|
|
2216
|
+
},
|
|
2217
|
+
{
|
|
2218
|
+
name: 'optionSource.statusPropertyPath',
|
|
2219
|
+
label: 'Campo de status',
|
|
2220
|
+
editorType: 'text',
|
|
2221
|
+
group: 'Exibicao de entidade',
|
|
2222
|
+
hint: 'Campo que informa status operacional da entidade.',
|
|
2223
|
+
},
|
|
2224
|
+
{
|
|
2225
|
+
name: 'optionSource.disabledPropertyPath',
|
|
2226
|
+
label: 'Campo disabled',
|
|
2227
|
+
editorType: 'text',
|
|
2228
|
+
group: 'Exibicao de entidade',
|
|
2229
|
+
hint: 'Campo booleano que indica bloqueio visual ou operacional da entidade.',
|
|
2230
|
+
},
|
|
2231
|
+
{
|
|
2232
|
+
name: 'optionSource.disabledReasonPropertyPath',
|
|
2233
|
+
label: 'Campo de motivo bloqueado',
|
|
2234
|
+
editorType: 'text',
|
|
2235
|
+
group: 'Exibicao de entidade',
|
|
2236
|
+
hint: 'Campo opcional com mensagem de bloqueio quando selectable=false.',
|
|
2237
|
+
},
|
|
2238
|
+
{
|
|
2239
|
+
name: 'selectedLayout',
|
|
2240
|
+
label: 'Layout selecionado',
|
|
2241
|
+
editorType: 'select',
|
|
2242
|
+
group: 'Exibicao de entidade',
|
|
2243
|
+
options: [
|
|
2244
|
+
{ value: 'card', text: 'card' },
|
|
2245
|
+
{ value: 'inline', text: 'inline' },
|
|
2246
|
+
{ value: 'compact', text: 'compact' },
|
|
2247
|
+
{ value: 'token', text: 'token' },
|
|
2248
|
+
],
|
|
2249
|
+
hint: 'Layout do item selecionado no trigger ou card principal.',
|
|
2250
|
+
},
|
|
2251
|
+
{
|
|
2252
|
+
name: 'resultLayout',
|
|
2253
|
+
label: 'Layout dos resultados',
|
|
2254
|
+
editorType: 'select',
|
|
2255
|
+
group: 'Exibicao de entidade',
|
|
2256
|
+
options: [
|
|
2257
|
+
{ value: 'list', text: 'list' },
|
|
2258
|
+
{ value: 'denseList', text: 'denseList' },
|
|
2259
|
+
{ value: 'table', text: 'table' },
|
|
2260
|
+
{ value: 'card', text: 'card' },
|
|
2261
|
+
],
|
|
2262
|
+
hint: 'Layout principal usado na lista inline e no dialog.',
|
|
2263
|
+
},
|
|
2264
|
+
{
|
|
2265
|
+
name: 'showCode',
|
|
2266
|
+
label: 'Mostrar codigo',
|
|
2267
|
+
editorType: 'checkbox',
|
|
2268
|
+
group: 'Exibicao de entidade',
|
|
2269
|
+
},
|
|
2270
|
+
{
|
|
2271
|
+
name: 'showDescription',
|
|
2272
|
+
label: 'Mostrar descricao',
|
|
2273
|
+
editorType: 'checkbox',
|
|
2274
|
+
group: 'Exibicao de entidade',
|
|
2275
|
+
},
|
|
2276
|
+
{
|
|
2277
|
+
name: 'showStatus',
|
|
2278
|
+
label: 'Mostrar status',
|
|
2279
|
+
editorType: 'checkbox',
|
|
2280
|
+
group: 'Exibicao de entidade',
|
|
2281
|
+
},
|
|
2282
|
+
{
|
|
2283
|
+
name: 'showAvatar',
|
|
2284
|
+
label: 'Mostrar avatar',
|
|
2285
|
+
editorType: 'checkbox',
|
|
2286
|
+
group: 'Exibicao de entidade',
|
|
2287
|
+
},
|
|
2288
|
+
{
|
|
2289
|
+
name: 'showBadges',
|
|
2290
|
+
label: 'Mostrar badges',
|
|
2291
|
+
editorType: 'checkbox',
|
|
2292
|
+
group: 'Exibicao de entidade',
|
|
2293
|
+
},
|
|
2294
|
+
{
|
|
2295
|
+
name: 'showDisabledReason',
|
|
2296
|
+
label: 'Mostrar motivo bloqueado',
|
|
2297
|
+
editorType: 'checkbox',
|
|
2298
|
+
group: 'Exibicao de entidade',
|
|
2299
|
+
},
|
|
2300
|
+
{
|
|
2301
|
+
name: 'showResultCount',
|
|
2302
|
+
label: 'Mostrar contador de resultados',
|
|
2303
|
+
editorType: 'checkbox',
|
|
2304
|
+
group: 'Exibicao de entidade',
|
|
2305
|
+
},
|
|
2306
|
+
{
|
|
2307
|
+
name: 'statusToneMap',
|
|
2308
|
+
label: 'Mapa de tons de status',
|
|
2309
|
+
editorType: 'textarea',
|
|
2310
|
+
group: 'Exibicao de entidade',
|
|
2311
|
+
hint: 'JSON object. Ex.: { \"ACTIVE\": \"success\", \"BLOCKED\": \"danger\" }.',
|
|
2312
|
+
},
|
|
2313
|
+
{
|
|
2314
|
+
name: 'badgeKeys',
|
|
2315
|
+
label: 'Chaves de badges',
|
|
2316
|
+
editorType: 'textarea',
|
|
2317
|
+
group: 'Exibicao de entidade',
|
|
2318
|
+
hint: 'JSON array ou uma linha por chave complementar de badge.',
|
|
2319
|
+
},
|
|
2320
|
+
{
|
|
2321
|
+
name: 'maxVisibleBadges',
|
|
2322
|
+
label: 'Maximo de badges visiveis',
|
|
2323
|
+
editorType: 'number',
|
|
2324
|
+
group: 'Exibicao de entidade',
|
|
2325
|
+
hint: 'Limita a quantidade de badges visiveis antes do overflow.',
|
|
2326
|
+
},
|
|
2327
|
+
{
|
|
2328
|
+
name: 'optionSource.searchPropertyPaths',
|
|
2329
|
+
label: 'Campos de busca',
|
|
2330
|
+
editorType: 'textarea',
|
|
2331
|
+
group: 'Busca',
|
|
2332
|
+
hint: 'JSON array ou uma linha por campo pesquisavel.',
|
|
2333
|
+
},
|
|
2334
|
+
{
|
|
2335
|
+
name: 'optionSource.searchMode',
|
|
2336
|
+
label: 'Modo de busca',
|
|
2337
|
+
editorType: 'select',
|
|
2338
|
+
group: 'Busca',
|
|
2339
|
+
options: [
|
|
2340
|
+
{ value: 'contains', text: 'contains' },
|
|
2341
|
+
{ value: 'startsWith', text: 'startsWith' },
|
|
2342
|
+
{ value: 'exact', text: 'exact' },
|
|
2343
|
+
],
|
|
2344
|
+
hint: 'Semantica de busca publicada pelo backend para esta fonte.',
|
|
2345
|
+
},
|
|
2346
|
+
{
|
|
2347
|
+
name: 'optionSource.pageSize',
|
|
2348
|
+
label: 'Tamanho da pagina',
|
|
2349
|
+
editorType: 'number',
|
|
2350
|
+
group: 'Busca',
|
|
2351
|
+
hint: 'Quantidade sugerida de resultados por pagina.',
|
|
2352
|
+
},
|
|
2353
|
+
{
|
|
2354
|
+
name: 'optionSource.includeIds',
|
|
2355
|
+
label: 'Incluir IDs selecionados',
|
|
2356
|
+
editorType: 'checkbox',
|
|
2357
|
+
group: 'Busca',
|
|
2358
|
+
defaultValue: true,
|
|
2359
|
+
hint: 'Mantem entidades ja selecionadas visiveis durante busca paginada.',
|
|
2360
|
+
},
|
|
2361
|
+
{
|
|
2362
|
+
name: 'optionSource.filtering.availableFilters',
|
|
2363
|
+
label: 'Filtros disponiveis',
|
|
2364
|
+
editorType: 'textarea',
|
|
2365
|
+
group: 'Busca',
|
|
2366
|
+
hint: 'JSON array de filtros estruturados. Ex.: [{ \"field\": \"status\", \"type\": \"enum\", \"operators\": [\"in\"] }].',
|
|
2367
|
+
},
|
|
2368
|
+
{
|
|
2369
|
+
name: 'optionSource.filtering.defaultFilters',
|
|
2370
|
+
label: 'Filtros padrao',
|
|
2371
|
+
editorType: 'textarea',
|
|
2372
|
+
group: 'Busca',
|
|
2373
|
+
hint: 'JSON object. Ex.: { \"status\": [\"ACTIVE\"] }.',
|
|
2374
|
+
},
|
|
2375
|
+
{
|
|
2376
|
+
name: 'optionSource.filtering.sortOptions',
|
|
2377
|
+
label: 'Ordenacoes disponiveis',
|
|
2378
|
+
editorType: 'textarea',
|
|
2379
|
+
group: 'Busca',
|
|
2380
|
+
hint: 'JSON array. Ex.: [{ \"key\": \"code:asc\", \"field\": \"code\", \"direction\": \"asc\", \"label\": \"Codigo\" }].',
|
|
2381
|
+
},
|
|
2382
|
+
{
|
|
2383
|
+
name: 'optionSource.filtering.defaultSort',
|
|
2384
|
+
label: 'Ordenacao padrao',
|
|
2385
|
+
editorType: 'text',
|
|
2386
|
+
group: 'Busca',
|
|
2387
|
+
hint: 'Chave default do sort declarada em sortOptions.',
|
|
2388
|
+
},
|
|
2389
|
+
{
|
|
2390
|
+
name: 'optionSource.filtering.quickFilterFields',
|
|
2391
|
+
label: 'Campos de filtro rapido',
|
|
2392
|
+
editorType: 'textarea',
|
|
2393
|
+
group: 'Busca',
|
|
2394
|
+
hint: 'JSON array ou uma linha por campo rapido no toolbar do lookup.',
|
|
2395
|
+
},
|
|
2396
|
+
{
|
|
2397
|
+
name: 'optionSource.filtering.searchPlaceholder',
|
|
2398
|
+
label: 'Placeholder canonico de busca',
|
|
2399
|
+
editorType: 'text',
|
|
2400
|
+
group: 'Busca',
|
|
2401
|
+
hint: 'Fallback do optionSource.filtering quando o campo nao sobrescreve searchPlaceholder.',
|
|
2402
|
+
},
|
|
2403
|
+
{
|
|
2404
|
+
name: 'optionSource.dependsOn',
|
|
2405
|
+
label: 'Dependencias',
|
|
2406
|
+
editorType: 'textarea',
|
|
2407
|
+
group: 'Dependencias',
|
|
2408
|
+
hint: 'JSON array ou uma linha por campo do formulario que filtra o lookup.',
|
|
2409
|
+
},
|
|
2410
|
+
{
|
|
2411
|
+
name: 'optionSource.dependencyFilterMap',
|
|
2412
|
+
label: 'Mapa de filtros',
|
|
2413
|
+
editorType: 'textarea',
|
|
2414
|
+
group: 'Dependencias',
|
|
2415
|
+
hint: 'JSON object. Ex.: { "empresaId": "companyId", "fornecedorId": "supplierId" }.',
|
|
2416
|
+
},
|
|
2417
|
+
{
|
|
2418
|
+
name: 'optionSource.selectionPolicy.statusPropertyPath',
|
|
2419
|
+
label: 'Politica: campo de status',
|
|
2420
|
+
editorType: 'text',
|
|
2421
|
+
group: 'Politica de selecao',
|
|
2422
|
+
hint: 'Campo usado para aplicar allowedStatuses ou blockedStatuses.',
|
|
2423
|
+
},
|
|
2424
|
+
{
|
|
2425
|
+
name: 'optionSource.selectionPolicy.selectablePropertyPath',
|
|
2426
|
+
label: 'Politica: campo selectable',
|
|
2427
|
+
editorType: 'text',
|
|
2428
|
+
group: 'Politica de selecao',
|
|
2429
|
+
hint: 'Campo booleano publicado pelo backend para indicar se a entidade pode ser selecionada.',
|
|
2430
|
+
},
|
|
2431
|
+
{
|
|
2432
|
+
name: 'optionSource.selectionPolicy.allowedStatuses',
|
|
2433
|
+
label: 'Status permitidos',
|
|
2434
|
+
editorType: 'textarea',
|
|
2435
|
+
group: 'Politica de selecao',
|
|
2436
|
+
hint: 'JSON array ou uma linha por status. Ex.: ACTIVE, SIGNED.',
|
|
2437
|
+
},
|
|
2438
|
+
{
|
|
2439
|
+
name: 'optionSource.selectionPolicy.blockedStatuses',
|
|
2440
|
+
label: 'Status bloqueados',
|
|
2441
|
+
editorType: 'textarea',
|
|
2442
|
+
group: 'Politica de selecao',
|
|
2443
|
+
hint: 'JSON array ou uma linha por status.',
|
|
2444
|
+
},
|
|
2445
|
+
{
|
|
2446
|
+
name: 'optionSource.selectionPolicy.disabledReasonTemplate',
|
|
2447
|
+
label: 'Template de motivo bloqueado',
|
|
2448
|
+
editorType: 'text',
|
|
2449
|
+
group: 'Politica de selecao',
|
|
2450
|
+
hint: 'Mensagem template exibida quando a entidade nao pode ser selecionada.',
|
|
2451
|
+
},
|
|
2452
|
+
{
|
|
2453
|
+
name: 'optionSource.selectionPolicy.validationMessageTemplate',
|
|
2454
|
+
label: 'Template de validacao',
|
|
2455
|
+
editorType: 'text',
|
|
2456
|
+
group: 'Politica de selecao',
|
|
2457
|
+
hint: 'Mensagem template usada em validacoes de selecao.',
|
|
2458
|
+
},
|
|
2459
|
+
{
|
|
2460
|
+
name: 'optionSource.selectionPolicy.allowRetainInvalidExistingValue',
|
|
2461
|
+
label: 'Manter valor invalido existente',
|
|
2462
|
+
editorType: 'checkbox',
|
|
2463
|
+
group: 'Politica de selecao',
|
|
2464
|
+
hint: 'Exibe entidade historica ja salva mesmo quando nao pode ser selecionada em novos registros.',
|
|
2465
|
+
},
|
|
2466
|
+
{
|
|
2467
|
+
name: 'optionSource.capabilities.filter',
|
|
2468
|
+
label: 'Capability: filter',
|
|
2469
|
+
editorType: 'checkbox',
|
|
2470
|
+
group: 'Capabilities',
|
|
2471
|
+
defaultValue: true,
|
|
2472
|
+
},
|
|
2473
|
+
{
|
|
2474
|
+
name: 'optionSource.capabilities.byIds',
|
|
2475
|
+
label: 'Capability: by-ids',
|
|
2476
|
+
editorType: 'checkbox',
|
|
2477
|
+
group: 'Capabilities',
|
|
2478
|
+
defaultValue: true,
|
|
2479
|
+
},
|
|
2480
|
+
{
|
|
2481
|
+
name: 'optionSource.capabilities.navigateToDetail',
|
|
2482
|
+
label: 'Capability: detalhe',
|
|
2483
|
+
editorType: 'checkbox',
|
|
2484
|
+
group: 'Capabilities',
|
|
2485
|
+
},
|
|
2486
|
+
{
|
|
2487
|
+
name: 'optionSource.capabilities.detail',
|
|
2488
|
+
label: 'Capability: endpoint de detalhe',
|
|
2489
|
+
editorType: 'checkbox',
|
|
2490
|
+
group: 'Capabilities',
|
|
2491
|
+
},
|
|
2492
|
+
{
|
|
2493
|
+
name: 'optionSource.capabilities.edit',
|
|
2494
|
+
label: 'Capability: editar',
|
|
2495
|
+
editorType: 'checkbox',
|
|
2496
|
+
group: 'Capabilities',
|
|
2497
|
+
},
|
|
2498
|
+
{
|
|
2499
|
+
name: 'optionSource.capabilities.create',
|
|
2500
|
+
label: 'Capability: criar',
|
|
2501
|
+
editorType: 'checkbox',
|
|
2502
|
+
group: 'Capabilities',
|
|
2503
|
+
},
|
|
2504
|
+
{
|
|
2505
|
+
name: 'optionSource.capabilities.multiSelect',
|
|
2506
|
+
label: 'Capability: multi-select',
|
|
2507
|
+
editorType: 'checkbox',
|
|
2508
|
+
group: 'Capabilities',
|
|
2509
|
+
},
|
|
2510
|
+
{
|
|
2511
|
+
name: 'optionSource.capabilities.recent',
|
|
2512
|
+
label: 'Capability: recentes',
|
|
2513
|
+
editorType: 'checkbox',
|
|
2514
|
+
group: 'Capabilities',
|
|
2515
|
+
},
|
|
2516
|
+
{
|
|
2517
|
+
name: 'optionSource.capabilities.favorites',
|
|
2518
|
+
label: 'Capability: favoritos',
|
|
2519
|
+
editorType: 'checkbox',
|
|
2520
|
+
group: 'Capabilities',
|
|
2521
|
+
},
|
|
2522
|
+
{
|
|
2523
|
+
name: 'optionSource.capabilities.auditSnapshot',
|
|
2524
|
+
label: 'Capability: snapshot de auditoria',
|
|
2525
|
+
editorType: 'checkbox',
|
|
2526
|
+
group: 'Capabilities',
|
|
2527
|
+
},
|
|
2528
|
+
{
|
|
2529
|
+
name: 'optionSource.detail.hrefTemplate',
|
|
2530
|
+
label: 'Template de detalhe',
|
|
2531
|
+
editorType: 'text',
|
|
2532
|
+
group: 'Navegacao',
|
|
2533
|
+
hint: 'Template de detalhe, por exemplo /contracts/{id}.',
|
|
2534
|
+
},
|
|
1152
2535
|
{
|
|
1153
|
-
name: '
|
|
1154
|
-
label: '
|
|
1155
|
-
editorType: '
|
|
1156
|
-
group: '
|
|
1157
|
-
|
|
1158
|
-
{ value: 'fill', text: 'Fill' },
|
|
1159
|
-
{ value: 'outline', text: 'Outline' },
|
|
1160
|
-
],
|
|
2536
|
+
name: 'optionSource.detail.routeTemplate',
|
|
2537
|
+
label: 'Template de rota',
|
|
2538
|
+
editorType: 'text',
|
|
2539
|
+
group: 'Navegacao',
|
|
2540
|
+
hint: 'Template de rota de detalhe, por exemplo /contracts/:id.',
|
|
1161
2541
|
},
|
|
1162
2542
|
{
|
|
1163
|
-
name: '
|
|
1164
|
-
label: '
|
|
2543
|
+
name: 'optionSource.detail.openDetailMode',
|
|
2544
|
+
label: 'Modo de detalhe',
|
|
1165
2545
|
editorType: 'select',
|
|
1166
|
-
group: '
|
|
2546
|
+
group: 'Navegacao',
|
|
1167
2547
|
options: [
|
|
1168
|
-
{ value: '
|
|
1169
|
-
{ value: '
|
|
1170
|
-
{ value: '
|
|
2548
|
+
{ value: 'samePage', text: 'samePage' },
|
|
2549
|
+
{ value: 'newTab', text: 'newTab' },
|
|
2550
|
+
{ value: 'drawer', text: 'Drawer' },
|
|
2551
|
+
{ value: 'modal', text: 'Modal' },
|
|
1171
2552
|
],
|
|
1172
2553
|
},
|
|
1173
2554
|
{
|
|
1174
|
-
name: '
|
|
1175
|
-
label: '
|
|
1176
|
-
editorType: '
|
|
1177
|
-
group: '
|
|
1178
|
-
|
|
1179
|
-
{ value: 'auto', text: 'Auto' },
|
|
1180
|
-
{ value: 'always', text: 'Sempre' },
|
|
1181
|
-
],
|
|
2555
|
+
name: 'optionSource.create.hrefTemplate',
|
|
2556
|
+
label: 'Template create href',
|
|
2557
|
+
editorType: 'text',
|
|
2558
|
+
group: 'Navegacao',
|
|
2559
|
+
hint: 'Template opcional de criacao publicado pelo optionSource.',
|
|
1182
2560
|
},
|
|
1183
2561
|
{
|
|
1184
|
-
name: '
|
|
1185
|
-
label: '
|
|
2562
|
+
name: 'optionSource.create.routeTemplate',
|
|
2563
|
+
label: 'Template create route',
|
|
2564
|
+
editorType: 'text',
|
|
2565
|
+
group: 'Navegacao',
|
|
2566
|
+
hint: 'Rota opcional de criacao, por exemplo /suppliers/new.',
|
|
2567
|
+
},
|
|
2568
|
+
{
|
|
2569
|
+
name: 'optionSource.create.openMode',
|
|
2570
|
+
label: 'Modo de create',
|
|
1186
2571
|
editorType: 'select',
|
|
1187
|
-
group: '
|
|
2572
|
+
group: 'Navegacao',
|
|
1188
2573
|
options: [
|
|
1189
|
-
{ value: '
|
|
1190
|
-
{ value: '
|
|
2574
|
+
{ value: 'samePage', text: 'samePage' },
|
|
2575
|
+
{ value: 'newTab', text: 'newTab' },
|
|
2576
|
+
{ value: 'drawer', text: 'Drawer' },
|
|
2577
|
+
{ value: 'modal', text: 'Modal' },
|
|
1191
2578
|
],
|
|
1192
2579
|
},
|
|
1193
|
-
{ name: 'inlineAutoSize.minWidth', label: 'Inline: largura mínima (desktop)', editorType: 'number', group: 'Aparência', row: 'appearance.inlineAutoSizeDesktop', inline: true, hint: 'Largura mínima em px para desktop.' },
|
|
1194
|
-
{ name: 'inlineAutoSize.maxWidth', label: 'Inline: largura máxima (desktop)', editorType: 'number', group: 'Aparência', row: 'appearance.inlineAutoSizeDesktop', inline: true, hint: 'Largura máxima em px para desktop.' },
|
|
1195
|
-
{ name: 'inlineAutoSize.minWidthMobile', label: 'Inline: largura mínima (mobile)', editorType: 'number', group: 'Aparência', row: 'appearance.inlineAutoSizeMobile', inline: true, hint: 'Largura mínima em px para mobile.' },
|
|
1196
|
-
{ name: 'inlineAutoSize.maxWidthMobile', label: 'Inline: largura máxima (mobile)', editorType: 'number', group: 'Aparência', row: 'appearance.inlineAutoSizeMobile', inline: true, hint: 'Largura máxima em px para mobile.' },
|
|
1197
|
-
{ name: 'materialDesign.hideRequiredMarker', label: 'Ocultar * obrigatório', editorType: 'checkbox', group: 'Aparência' },
|
|
1198
2580
|
{
|
|
1199
|
-
name: '
|
|
1200
|
-
label: '
|
|
2581
|
+
name: 'dialog.enabled',
|
|
2582
|
+
label: 'Dialog habilitado',
|
|
2583
|
+
editorType: 'checkbox',
|
|
2584
|
+
group: 'Dialogo avancado',
|
|
2585
|
+
},
|
|
2586
|
+
{
|
|
2587
|
+
name: 'dialog.title',
|
|
2588
|
+
label: 'Titulo do dialog',
|
|
2589
|
+
editorType: 'text',
|
|
2590
|
+
group: 'Dialogo avancado',
|
|
2591
|
+
},
|
|
2592
|
+
{
|
|
2593
|
+
name: 'dialog.size',
|
|
2594
|
+
label: 'Tamanho do dialog',
|
|
1201
2595
|
editorType: 'select',
|
|
1202
|
-
group: '
|
|
2596
|
+
group: 'Dialogo avancado',
|
|
1203
2597
|
options: [
|
|
1204
|
-
{ value: '
|
|
1205
|
-
{ value: '
|
|
1206
|
-
{ value: '
|
|
1207
|
-
{ value: '
|
|
2598
|
+
{ value: 'sm', text: 'sm' },
|
|
2599
|
+
{ value: 'md', text: 'md' },
|
|
2600
|
+
{ value: 'lg', text: 'lg' },
|
|
2601
|
+
{ value: 'xl', text: 'xl' },
|
|
2602
|
+
{ value: 'full', text: 'full' },
|
|
1208
2603
|
],
|
|
1209
2604
|
},
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
2605
|
+
{
|
|
2606
|
+
name: 'dialog.previewPanel',
|
|
2607
|
+
label: 'Preview panel',
|
|
2608
|
+
editorType: 'checkbox',
|
|
2609
|
+
group: 'Dialogo avancado',
|
|
2610
|
+
},
|
|
2611
|
+
{
|
|
2612
|
+
name: 'dialog.allowColumnChooser',
|
|
2613
|
+
label: 'Permitir seletor de colunas',
|
|
2614
|
+
editorType: 'checkbox',
|
|
2615
|
+
group: 'Dialogo avancado',
|
|
2616
|
+
},
|
|
2617
|
+
{
|
|
2618
|
+
name: 'dialog.allowSavedViews',
|
|
2619
|
+
label: 'Permitir views salvas',
|
|
2620
|
+
editorType: 'checkbox',
|
|
2621
|
+
group: 'Dialogo avancado',
|
|
2622
|
+
},
|
|
2623
|
+
{
|
|
2624
|
+
name: 'dialog.resultColumns',
|
|
2625
|
+
label: 'Colunas do resultado',
|
|
2626
|
+
editorType: 'textarea',
|
|
2627
|
+
group: 'Dialogo avancado',
|
|
2628
|
+
hint: 'JSON array. Ex.: [{ \"field\": \"code\", \"label\": \"Codigo\", \"kind\": \"code\", \"width\": \"140px\" }].',
|
|
2629
|
+
},
|
|
2630
|
+
{
|
|
2631
|
+
name: 'dialog.openActionLabel',
|
|
2632
|
+
label: 'Label abrir dialog',
|
|
2633
|
+
editorType: 'text',
|
|
2634
|
+
group: 'Dialogo avancado',
|
|
2635
|
+
},
|
|
2636
|
+
{
|
|
2637
|
+
name: 'dialog.applyActionLabel',
|
|
2638
|
+
label: 'Label aplicar dialog',
|
|
2639
|
+
editorType: 'text',
|
|
2640
|
+
group: 'Dialogo avancado',
|
|
2641
|
+
},
|
|
2642
|
+
{
|
|
2643
|
+
name: 'dialog.cancelActionLabel',
|
|
2644
|
+
label: 'Label cancelar dialog',
|
|
2645
|
+
editorType: 'text',
|
|
2646
|
+
group: 'Dialogo avancado',
|
|
2647
|
+
},
|
|
2648
|
+
{
|
|
2649
|
+
name: 'detailActionLabel',
|
|
2650
|
+
label: 'Label detalhe',
|
|
2651
|
+
editorType: 'text',
|
|
2652
|
+
group: 'Acoes do lookup',
|
|
2653
|
+
},
|
|
2654
|
+
{
|
|
2655
|
+
name: 'changeActionLabel',
|
|
2656
|
+
label: 'Label trocar',
|
|
2657
|
+
editorType: 'text',
|
|
2658
|
+
group: 'Acoes do lookup',
|
|
2659
|
+
},
|
|
2660
|
+
{
|
|
2661
|
+
name: 'copyCodeActionLabel',
|
|
2662
|
+
label: 'Label copiar codigo',
|
|
2663
|
+
editorType: 'text',
|
|
2664
|
+
group: 'Acoes do lookup',
|
|
2665
|
+
},
|
|
2666
|
+
{
|
|
2667
|
+
name: 'copyIdActionLabel',
|
|
2668
|
+
label: 'Label copiar ID',
|
|
2669
|
+
editorType: 'text',
|
|
2670
|
+
group: 'Acoes do lookup',
|
|
2671
|
+
},
|
|
2672
|
+
{
|
|
2673
|
+
name: 'createActionLabel',
|
|
2674
|
+
label: 'Label criar',
|
|
2675
|
+
editorType: 'text',
|
|
2676
|
+
group: 'Acoes do lookup',
|
|
2677
|
+
},
|
|
2678
|
+
{
|
|
2679
|
+
name: 'clearActionLabel',
|
|
2680
|
+
label: 'Label limpar',
|
|
2681
|
+
editorType: 'text',
|
|
2682
|
+
group: 'Acoes do lookup',
|
|
2683
|
+
},
|
|
2684
|
+
{
|
|
2685
|
+
name: 'actions.showDetail',
|
|
2686
|
+
label: 'Acao: detalhe',
|
|
2687
|
+
editorType: 'checkbox',
|
|
2688
|
+
group: 'Acoes do lookup',
|
|
2689
|
+
},
|
|
2690
|
+
{
|
|
2691
|
+
name: 'actions.showChange',
|
|
2692
|
+
label: 'Acao: trocar',
|
|
2693
|
+
editorType: 'checkbox',
|
|
2694
|
+
group: 'Acoes do lookup',
|
|
2695
|
+
},
|
|
2696
|
+
{
|
|
2697
|
+
name: 'actions.showCopyCode',
|
|
2698
|
+
label: 'Acao: copiar codigo',
|
|
2699
|
+
editorType: 'checkbox',
|
|
2700
|
+
group: 'Acoes do lookup',
|
|
2701
|
+
},
|
|
2702
|
+
{
|
|
2703
|
+
name: 'actions.showCopyId',
|
|
2704
|
+
label: 'Acao: copiar ID',
|
|
2705
|
+
editorType: 'checkbox',
|
|
2706
|
+
group: 'Acoes do lookup',
|
|
2707
|
+
},
|
|
2708
|
+
{
|
|
2709
|
+
name: 'actions.showCreate',
|
|
2710
|
+
label: 'Acao: criar',
|
|
2711
|
+
editorType: 'checkbox',
|
|
2712
|
+
group: 'Acoes do lookup',
|
|
2713
|
+
},
|
|
2714
|
+
{
|
|
2715
|
+
name: 'actions.showClear',
|
|
2716
|
+
label: 'Acao: limpar',
|
|
2717
|
+
editorType: 'checkbox',
|
|
2718
|
+
group: 'Acoes do lookup',
|
|
2719
|
+
},
|
|
1216
2720
|
];
|
|
1217
2721
|
|
|
1218
2722
|
const transferListProperties = [
|
|
@@ -3134,6 +4638,18 @@ const sliderProperties = [
|
|
|
3134
4638
|
// Formato/Comportamento
|
|
3135
4639
|
{ name: 'step', label: 'Step', editorType: 'number', group: 'Formato/Comportamento', hint: 'Incremento entre valores (ex.: 1, 0.5). Combine com min/max.' },
|
|
3136
4640
|
{ name: 'thumbLabel', label: 'Thumb label', editorType: 'checkbox', group: 'Formato/Comportamento', hint: 'Exibe valor sobre o thumb ao arrastar.' },
|
|
4641
|
+
{ name: 'valueLabelDisplay', label: 'Value label', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4642
|
+
{ value: 'off', text: 'Desligado' },
|
|
4643
|
+
{ value: 'auto', text: 'Ao interagir' },
|
|
4644
|
+
{ value: 'on', text: 'Sempre visivel' },
|
|
4645
|
+
], hint: 'Controla quando o valor aparece no thumb.' },
|
|
4646
|
+
{ name: 'valueLabelFormat', label: 'Formato do valor', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4647
|
+
{ value: 'number', text: 'Numero' },
|
|
4648
|
+
{ value: 'percent', text: 'Percentual' },
|
|
4649
|
+
{ value: 'currency', text: 'Moeda' },
|
|
4650
|
+
{ value: 'compact', text: 'Compacto' },
|
|
4651
|
+
{ value: 'storage', text: 'Armazenamento' },
|
|
4652
|
+
], hint: 'Formatter declarativo para labels e aria-valuetext.' },
|
|
3137
4653
|
{ name: 'vertical', label: 'Vertical', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'fmt.flags', inline: true, hint: 'Altera para eixo Y.' },
|
|
3138
4654
|
{ name: 'invert', label: 'Inverter', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'fmt.flags', inline: true, hint: 'Inverte direção (direita→esquerda).' },
|
|
3139
4655
|
{ name: 'showTicks', label: 'Ticks', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
@@ -3141,6 +4657,26 @@ const sliderProperties = [
|
|
|
3141
4657
|
{ value: true, text: 'Com ticks' },
|
|
3142
4658
|
{ value: 'auto', text: 'Auto' },
|
|
3143
4659
|
], hint: 'Marcas na trilha; auto calcula com base no step.' },
|
|
4660
|
+
{ name: 'marks', label: 'Marcadores (JSON)', editorType: 'textarea', group: 'Formato/Comportamento', hint: 'Array JSON. Ex.: [{"value":0,"label":"Baixo"},{"value":100,"label":"Alto","tone":"danger"}].' },
|
|
4661
|
+
{ name: 'semanticBands', label: 'Bandas semanticas (JSON)', editorType: 'textarea', group: 'Formato/Comportamento', hint: 'Array JSON. Ex.: [{"start":0,"end":40,"label":"Saudavel","tone":"success"}].' },
|
|
4662
|
+
{ name: 'distribution', label: 'Distribuicao (JSON)', editorType: 'textarea', group: 'Formato/Comportamento', hint: 'Array JSON ou config de histograma compacto. Ex.: {"colorMode":"gradient","bins":[4,8,16,12,6]}.' },
|
|
4663
|
+
{ name: 'track', label: 'Track', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4664
|
+
{ value: 'normal', text: 'Normal' },
|
|
4665
|
+
{ value: 'inverted', text: 'Invertido' },
|
|
4666
|
+
{ value: 'none', text: 'Sem track ativo' },
|
|
4667
|
+
], hint: 'Apresentacao visual do track.' },
|
|
4668
|
+
{ name: 'size', label: 'Tamanho', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4669
|
+
{ value: 'small', text: 'Pequeno' },
|
|
4670
|
+
{ value: 'medium', text: 'Medio' },
|
|
4671
|
+
{ value: 'large', text: 'Grande' },
|
|
4672
|
+
], hint: 'Densidade visual do slider.' },
|
|
4673
|
+
{ name: 'scale', label: 'Escala', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4674
|
+
{ value: 'linear', text: 'Linear' },
|
|
4675
|
+
{ value: 'percent', text: 'Percentual' },
|
|
4676
|
+
{ value: 'log', text: 'Logaritmica' },
|
|
4677
|
+
{ value: 'pow2', text: 'Potencia 2' },
|
|
4678
|
+
], hint: 'Escala declarativa para dominios nao lineares.' },
|
|
4679
|
+
{ name: 'shiftStep', label: 'Shift step', editorType: 'number', group: 'Formato/Comportamento', hint: 'Incremento acelerado para teclado.' },
|
|
3144
4680
|
{ name: 'discrete', label: 'Discreto', editorType: 'checkbox', group: 'Formato/Comportamento', hint: 'Exibe ticks/labels fixos para valores discretos.' },
|
|
3145
4681
|
// Validação
|
|
3146
4682
|
{ name: 'min', label: 'Mínimo', editorType: 'number', group: 'Validação', row: 'val.minmax', inline: true },
|
|
@@ -3180,11 +4716,44 @@ const rangeSliderProperties = [
|
|
|
3180
4716
|
], hint: 'Escolha faixa para dois marcadores.' },
|
|
3181
4717
|
{ name: 'step', label: 'Step', editorType: 'number', group: 'Formato/Comportamento', hint: 'Incremento entre valores (ex.: 1, 0.5). Combine com min/max.' },
|
|
3182
4718
|
{ name: 'thumbLabel', label: 'Thumb label', editorType: 'checkbox', group: 'Formato/Comportamento', hint: 'Exibe o valor sobre o thumb ao arrastar.' },
|
|
4719
|
+
{ name: 'valueLabelDisplay', label: 'Value label', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4720
|
+
{ value: 'off', text: 'Desligado' },
|
|
4721
|
+
{ value: 'auto', text: 'Ao interagir' },
|
|
4722
|
+
{ value: 'on', text: 'Sempre visivel' },
|
|
4723
|
+
], hint: 'Controla quando o valor aparece no thumb.' },
|
|
4724
|
+
{ name: 'valueLabelFormat', label: 'Formato do valor', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4725
|
+
{ value: 'number', text: 'Numero' },
|
|
4726
|
+
{ value: 'percent', text: 'Percentual' },
|
|
4727
|
+
{ value: 'currency', text: 'Moeda' },
|
|
4728
|
+
{ value: 'compact', text: 'Compacto' },
|
|
4729
|
+
{ value: 'storage', text: 'Armazenamento' },
|
|
4730
|
+
], hint: 'Formatter declarativo para labels e aria-valuetext.' },
|
|
3183
4731
|
{ name: 'showTicks', label: 'Ticks', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
3184
4732
|
{ value: false, text: 'Sem ticks' },
|
|
3185
4733
|
{ value: true, text: 'Com ticks' },
|
|
3186
4734
|
{ value: 'auto', text: 'Auto' },
|
|
3187
4735
|
], hint: 'Marcas na trilha; auto calcula com base no step.' },
|
|
4736
|
+
{ name: 'marks', label: 'Marcadores (JSON)', editorType: 'textarea', group: 'Formato/Comportamento', hint: 'Array JSON. Ex.: [{"value":0,"label":"Min"},{"value":100,"label":"Max","tone":"danger"}].' },
|
|
4737
|
+
{ name: 'semanticBands', label: 'Bandas semanticas (JSON)', editorType: 'textarea', group: 'Formato/Comportamento', hint: 'Array JSON. Ex.: [{"start":0,"end":40,"label":"Baixo","tone":"success"}].' },
|
|
4738
|
+
{ name: 'distribution', label: 'Distribuicao (JSON)', editorType: 'textarea', group: 'Formato/Comportamento', hint: 'Array JSON ou config de histograma compacto. Ex.: {"colorMode":"gradient","bins":[4,8,16,12,6]}.' },
|
|
4739
|
+
{ name: 'track', label: 'Track', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4740
|
+
{ value: 'normal', text: 'Normal' },
|
|
4741
|
+
{ value: 'inverted', text: 'Invertido' },
|
|
4742
|
+
{ value: 'none', text: 'Sem track ativo' },
|
|
4743
|
+
], hint: 'Apresentacao visual do track.' },
|
|
4744
|
+
{ name: 'size', label: 'Tamanho', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4745
|
+
{ value: 'small', text: 'Pequeno' },
|
|
4746
|
+
{ value: 'medium', text: 'Medio' },
|
|
4747
|
+
{ value: 'large', text: 'Grande' },
|
|
4748
|
+
], hint: 'Densidade visual do slider.' },
|
|
4749
|
+
{ name: 'scale', label: 'Escala', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4750
|
+
{ value: 'linear', text: 'Linear' },
|
|
4751
|
+
{ value: 'percent', text: 'Percentual' },
|
|
4752
|
+
{ value: 'log', text: 'Logaritmica' },
|
|
4753
|
+
{ value: 'pow2', text: 'Potencia 2' },
|
|
4754
|
+
], hint: 'Escala declarativa para dominios nao lineares.' },
|
|
4755
|
+
{ name: 'shiftStep', label: 'Shift step', editorType: 'number', group: 'Formato/Comportamento', hint: 'Incremento acelerado para teclado.' },
|
|
4756
|
+
{ name: 'disableThumbSwap', label: 'Bloquear troca dos thumbs', editorType: 'checkbox', group: 'Formato/Comportamento', hint: 'Mantem start/end sem permitir inversao visual no modo range.' },
|
|
3188
4757
|
{ name: 'discrete', label: 'Discreto', editorType: 'checkbox', group: 'Formato/Comportamento', hint: 'Mostra valor fixo por tick; use com showTicks.' },
|
|
3189
4758
|
{ name: 'readonly', label: 'Somente leitura', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.state', inline: true, hint: 'Mantém o valor renderizado sem permitir ajuste do slider.' },
|
|
3190
4759
|
{ name: 'disabled', label: 'Desabilitado', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.state', inline: true, hint: 'Desativa arraste, foco e presets rápidos.' },
|
|
@@ -5323,7 +6892,7 @@ const INLINE_FILTER_EDITOR_PROPERTIES_BY_CONTROL_TYPE = Object.freeze({
|
|
|
5323
6892
|
[INLINE_FILTER_CONTROL_TYPES.SELECT]: selectProperties,
|
|
5324
6893
|
[INLINE_FILTER_CONTROL_TYPES.SEARCHABLE_SELECT]: selectProperties,
|
|
5325
6894
|
[INLINE_FILTER_CONTROL_TYPES.ASYNC_SELECT]: selectProperties,
|
|
5326
|
-
[INLINE_FILTER_CONTROL_TYPES.ENTITY_LOOKUP]:
|
|
6895
|
+
[INLINE_FILTER_CONTROL_TYPES.ENTITY_LOOKUP]: entityLookupProperties,
|
|
5327
6896
|
[INLINE_FILTER_CONTROL_TYPES.AUTOCOMPLETE]: selectProperties,
|
|
5328
6897
|
[INLINE_FILTER_CONTROL_TYPES.MULTI_SELECT]: selectProperties,
|
|
5329
6898
|
[INLINE_FILTER_CONTROL_TYPES.INPUT]: inputProperties,
|
|
@@ -5366,6 +6935,7 @@ class FieldMetadataEditorComponent {
|
|
|
5366
6935
|
cancel = new EventEmitter();
|
|
5367
6936
|
form;
|
|
5368
6937
|
normalizedProps = [];
|
|
6938
|
+
renderedProps = [];
|
|
5369
6939
|
initialSnapshot = null;
|
|
5370
6940
|
baselineReady = false;
|
|
5371
6941
|
// Lista de tipos suportados no editor (curada para os tipos com config disponível)
|
|
@@ -5380,10 +6950,12 @@ class FieldMetadataEditorComponent {
|
|
|
5380
6950
|
{ value: FieldControlType.URL_INPUT, label: 'URL' },
|
|
5381
6951
|
{ value: FieldControlType.SEARCH_INPUT, label: 'Busca' },
|
|
5382
6952
|
{ value: FieldControlType.PHONE, label: 'Telefone' },
|
|
6953
|
+
{ value: FieldControlType.ARRAY_INPUT, label: 'Colecao editavel' },
|
|
5383
6954
|
{ value: FieldControlType.SELECT, label: 'Seleção (Select)' },
|
|
5384
6955
|
{ value: FieldControlType.MULTI_SELECT, label: 'Seleção múltipla' },
|
|
5385
6956
|
{ value: FieldControlType.SEARCHABLE_SELECT, label: 'Seleção buscável' },
|
|
5386
6957
|
{ value: FieldControlType.ASYNC_SELECT, label: 'Seleção assíncrona' },
|
|
6958
|
+
{ value: FieldControlType.ENTITY_LOOKUP, label: 'Entity Lookup' },
|
|
5387
6959
|
{ value: FieldControlType.AUTO_COMPLETE, label: 'Auto-completar' },
|
|
5388
6960
|
{ value: FieldControlType.SELECTION_LIST, label: 'Lista de seleção' },
|
|
5389
6961
|
{ value: FieldControlType.TREE_SELECT, label: 'Árvore (Tree Select)' },
|
|
@@ -5471,9 +7043,12 @@ class FieldMetadataEditorComponent {
|
|
|
5471
7043
|
return inlineProps;
|
|
5472
7044
|
if (controlType === FieldControlType.INPUT)
|
|
5473
7045
|
return inputProperties;
|
|
7046
|
+
if (controlType === FieldControlType.ARRAY_INPUT)
|
|
7047
|
+
return arrayProperties;
|
|
7048
|
+
if (controlTypeToken === 'entitylookup')
|
|
7049
|
+
return entityLookupProperties;
|
|
5474
7050
|
if (controlTypeToken === 'searchableselect' ||
|
|
5475
|
-
controlTypeToken === 'asyncselect'
|
|
5476
|
-
controlTypeToken === 'entitylookup')
|
|
7051
|
+
controlTypeToken === 'asyncselect')
|
|
5477
7052
|
return selectProperties;
|
|
5478
7053
|
if (controlType === FieldControlType.NUMERIC_TEXT_BOX)
|
|
5479
7054
|
return numberProperties;
|
|
@@ -5518,6 +7093,8 @@ class FieldMetadataEditorComponent {
|
|
|
5518
7093
|
controlType === FieldControlType.AUTO_COMPLETE ||
|
|
5519
7094
|
controlType === FieldControlType.SELECTION_LIST)
|
|
5520
7095
|
return selectProperties;
|
|
7096
|
+
if (controlType === FieldControlType.ENTITY_LOOKUP)
|
|
7097
|
+
return entityLookupProperties;
|
|
5521
7098
|
if (controlType === FieldControlType.TEXTAREA)
|
|
5522
7099
|
return textareaProperties;
|
|
5523
7100
|
if (controlType === FieldControlType.TRANSFER_LIST)
|
|
@@ -5594,11 +7171,14 @@ class FieldMetadataEditorComponent {
|
|
|
5594
7171
|
this.registry.register(this.controlType, inputProperties);
|
|
5595
7172
|
props = inputProperties;
|
|
5596
7173
|
}
|
|
7174
|
+
else if (controlTypeToken === 'entitylookup') {
|
|
7175
|
+
this.registry.register(this.controlType, entityLookupProperties);
|
|
7176
|
+
props = entityLookupProperties;
|
|
7177
|
+
}
|
|
5597
7178
|
else if (
|
|
5598
7179
|
// Aliases for select-like
|
|
5599
7180
|
controlTypeToken === 'searchableselect' ||
|
|
5600
|
-
controlTypeToken === 'asyncselect'
|
|
5601
|
-
controlTypeToken === 'entitylookup') {
|
|
7181
|
+
controlTypeToken === 'asyncselect') {
|
|
5602
7182
|
this.registry.register(this.controlType, selectProperties);
|
|
5603
7183
|
props = selectProperties;
|
|
5604
7184
|
}
|
|
@@ -5683,6 +7263,10 @@ class FieldMetadataEditorComponent {
|
|
|
5683
7263
|
this.registry.register(this.controlType, selectProperties);
|
|
5684
7264
|
props = selectProperties;
|
|
5685
7265
|
}
|
|
7266
|
+
else if (this.controlType === FieldControlType.ENTITY_LOOKUP) {
|
|
7267
|
+
this.registry.register(this.controlType, entityLookupProperties);
|
|
7268
|
+
props = entityLookupProperties;
|
|
7269
|
+
}
|
|
5686
7270
|
else if (this.controlType === FieldControlType.TEXTAREA) {
|
|
5687
7271
|
this.registry.register(this.controlType, textareaProperties);
|
|
5688
7272
|
props = textareaProperties;
|
|
@@ -5814,6 +7398,7 @@ class FieldMetadataEditorComponent {
|
|
|
5814
7398
|
}
|
|
5815
7399
|
}
|
|
5816
7400
|
catch { }
|
|
7401
|
+
this.renderedProps = this.buildRenderedProps(this.normalizedProps);
|
|
5817
7402
|
// Register contextual validators for INPUT/TEXTAREA (minLength <= maxLength)
|
|
5818
7403
|
if (this.controlType === FieldControlType.INPUT ||
|
|
5819
7404
|
this.controlType === FieldControlType.TEXTAREA) {
|
|
@@ -5844,7 +7429,7 @@ class FieldMetadataEditorComponent {
|
|
|
5844
7429
|
const name = p?.name;
|
|
5845
7430
|
if (!name)
|
|
5846
7431
|
continue;
|
|
5847
|
-
const val = getByPath(this.seed, name);
|
|
7432
|
+
const val = this.toEditorControlValue(p, getByPath(this.seed, name));
|
|
5848
7433
|
// Preserve false/0; skip only null/undefined
|
|
5849
7434
|
if (val !== undefined && val !== null) {
|
|
5850
7435
|
this.factory.setValueByPath(this.form, name, val);
|
|
@@ -6028,6 +7613,9 @@ class FieldMetadataEditorComponent {
|
|
|
6028
7613
|
});
|
|
6029
7614
|
}
|
|
6030
7615
|
apply() {
|
|
7616
|
+
if (!this.canEmitSettingsValue()) {
|
|
7617
|
+
return;
|
|
7618
|
+
}
|
|
6031
7619
|
const raw = this.factory.extractPatch(this.form);
|
|
6032
7620
|
const patch = this.buildDeltaPatch(raw, this.initialSnapshot);
|
|
6033
7621
|
this.normalizeKnownTextareaPatchValues(patch);
|
|
@@ -6055,6 +7643,9 @@ class FieldMetadataEditorComponent {
|
|
|
6055
7643
|
}
|
|
6056
7644
|
// Settings Panel value provider API
|
|
6057
7645
|
getSettingsValue() {
|
|
7646
|
+
if (!this.canEmitSettingsValue()) {
|
|
7647
|
+
return {};
|
|
7648
|
+
}
|
|
6058
7649
|
const raw = this.factory.extractPatch(this.form);
|
|
6059
7650
|
const patch = this.buildDeltaPatch(raw, this.initialSnapshot);
|
|
6060
7651
|
this.normalizeKnownTextareaPatchValues(patch);
|
|
@@ -6154,6 +7745,119 @@ class FieldMetadataEditorComponent {
|
|
|
6154
7745
|
patch.links = parsedLinks;
|
|
6155
7746
|
}
|
|
6156
7747
|
}
|
|
7748
|
+
this.normalizeEntityLookupPatchValues(patch);
|
|
7749
|
+
this.normalizeArrayPatchValues(patch);
|
|
7750
|
+
}
|
|
7751
|
+
normalizeEntityLookupPatchValues(patch) {
|
|
7752
|
+
const optionSource = patch?.optionSource;
|
|
7753
|
+
if (optionSource && typeof optionSource === 'object') {
|
|
7754
|
+
this.normalizeStringListProperty(optionSource, 'descriptionPropertyPaths');
|
|
7755
|
+
this.normalizeStringListProperty(optionSource, 'searchPropertyPaths');
|
|
7756
|
+
this.normalizeStringListProperty(optionSource, 'dependsOn');
|
|
7757
|
+
this.normalizeJsonObjectProperty(optionSource, 'dependencyFilterMap');
|
|
7758
|
+
this.normalizeJsonObjectProperty(optionSource, 'create');
|
|
7759
|
+
const selectionPolicy = optionSource.selectionPolicy;
|
|
7760
|
+
if (selectionPolicy && typeof selectionPolicy === 'object') {
|
|
7761
|
+
this.normalizeStringListProperty(selectionPolicy, 'allowedStatuses');
|
|
7762
|
+
this.normalizeStringListProperty(selectionPolicy, 'blockedStatuses');
|
|
7763
|
+
}
|
|
7764
|
+
const filtering = optionSource.filtering;
|
|
7765
|
+
if (filtering && typeof filtering === 'object') {
|
|
7766
|
+
this.normalizeJsonArrayProperty(filtering, 'availableFilters');
|
|
7767
|
+
this.normalizeJsonObjectProperty(filtering, 'defaultFilters');
|
|
7768
|
+
this.normalizeJsonArrayProperty(filtering, 'sortOptions');
|
|
7769
|
+
this.normalizeStringListProperty(filtering, 'quickFilterFields');
|
|
7770
|
+
}
|
|
7771
|
+
}
|
|
7772
|
+
this.normalizeStringListProperty(patch, 'badgeKeys');
|
|
7773
|
+
this.normalizeJsonObjectProperty(patch, 'statusToneMap');
|
|
7774
|
+
const dialog = patch?.dialog;
|
|
7775
|
+
if (dialog && typeof dialog === 'object') {
|
|
7776
|
+
this.normalizeJsonArrayProperty(dialog, 'resultColumns');
|
|
7777
|
+
}
|
|
7778
|
+
}
|
|
7779
|
+
normalizeStringListProperty(target, key) {
|
|
7780
|
+
if (!target || typeof target !== 'object' || typeof target[key] !== 'string') {
|
|
7781
|
+
return;
|
|
7782
|
+
}
|
|
7783
|
+
const parsed = this.parseStringListTextarea(target[key]);
|
|
7784
|
+
if (parsed) {
|
|
7785
|
+
target[key] = parsed;
|
|
7786
|
+
}
|
|
7787
|
+
}
|
|
7788
|
+
normalizeJsonObjectProperty(target, key) {
|
|
7789
|
+
if (!target || typeof target !== 'object' || typeof target[key] !== 'string') {
|
|
7790
|
+
return;
|
|
7791
|
+
}
|
|
7792
|
+
const parsed = this.tryParseJson(String(target[key]).trim());
|
|
7793
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
7794
|
+
target[key] = parsed;
|
|
7795
|
+
}
|
|
7796
|
+
}
|
|
7797
|
+
normalizeJsonArrayProperty(target, key) {
|
|
7798
|
+
if (!target || typeof target !== 'object' || typeof target[key] !== 'string') {
|
|
7799
|
+
return;
|
|
7800
|
+
}
|
|
7801
|
+
const parsed = this.parseArrayTextarea(target[key]);
|
|
7802
|
+
if (parsed) {
|
|
7803
|
+
target[key] = parsed;
|
|
7804
|
+
}
|
|
7805
|
+
}
|
|
7806
|
+
normalizeArrayPatchValues(patch) {
|
|
7807
|
+
const arrayPatch = patch?.array;
|
|
7808
|
+
if (!arrayPatch || typeof arrayPatch !== 'object') {
|
|
7809
|
+
return;
|
|
7810
|
+
}
|
|
7811
|
+
const collectionValidation = arrayPatch.collectionValidation;
|
|
7812
|
+
if (collectionValidation &&
|
|
7813
|
+
typeof collectionValidation === 'object' &&
|
|
7814
|
+
typeof collectionValidation.uniqueBy === 'string') {
|
|
7815
|
+
const parsedUniqueBy = this.parseStringListTextarea(collectionValidation.uniqueBy);
|
|
7816
|
+
if (parsedUniqueBy) {
|
|
7817
|
+
collectionValidation.uniqueBy = parsedUniqueBy;
|
|
7818
|
+
}
|
|
7819
|
+
}
|
|
7820
|
+
this.normalizeArrayCountRuleValue(collectionValidation?.exactlyOne);
|
|
7821
|
+
this.normalizeArrayCountRuleValue(collectionValidation?.atLeastOne);
|
|
7822
|
+
const itemSchema = arrayPatch.itemSchema;
|
|
7823
|
+
if (itemSchema &&
|
|
7824
|
+
typeof itemSchema === 'object' &&
|
|
7825
|
+
typeof itemSchema.fields === 'string') {
|
|
7826
|
+
const parsedFields = this.parseArrayTextarea(itemSchema.fields);
|
|
7827
|
+
if (parsedFields) {
|
|
7828
|
+
itemSchema.fields = parsedFields;
|
|
7829
|
+
}
|
|
7830
|
+
}
|
|
7831
|
+
if (typeof patch.defaultValue === 'string') {
|
|
7832
|
+
const parsedDefaultValue = this.parseArrayTextarea(patch.defaultValue);
|
|
7833
|
+
if (parsedDefaultValue) {
|
|
7834
|
+
patch.defaultValue = parsedDefaultValue;
|
|
7835
|
+
}
|
|
7836
|
+
}
|
|
7837
|
+
}
|
|
7838
|
+
normalizeArrayCountRuleValue(rule) {
|
|
7839
|
+
if (!rule || typeof rule !== 'object' || typeof rule.value !== 'string') {
|
|
7840
|
+
return;
|
|
7841
|
+
}
|
|
7842
|
+
const raw = rule.value.trim();
|
|
7843
|
+
if (!raw) {
|
|
7844
|
+
return;
|
|
7845
|
+
}
|
|
7846
|
+
try {
|
|
7847
|
+
rule.value = JSON.parse(raw);
|
|
7848
|
+
}
|
|
7849
|
+
catch {
|
|
7850
|
+
rule.value = raw;
|
|
7851
|
+
}
|
|
7852
|
+
}
|
|
7853
|
+
toEditorControlValue(prop, value) {
|
|
7854
|
+
if (prop?.editorType === 'textarea' &&
|
|
7855
|
+
value !== undefined &&
|
|
7856
|
+
value !== null &&
|
|
7857
|
+
typeof value !== 'string') {
|
|
7858
|
+
return JSON.stringify(value, null, 2);
|
|
7859
|
+
}
|
|
7860
|
+
return value;
|
|
6157
7861
|
}
|
|
6158
7862
|
/**
|
|
6159
7863
|
* Interpreta textarea como lista simples de strings:
|
|
@@ -6207,7 +7911,11 @@ class FieldMetadataEditorComponent {
|
|
|
6207
7911
|
}
|
|
6208
7912
|
}
|
|
6209
7913
|
reset() {
|
|
6210
|
-
this.
|
|
7914
|
+
const snapshot = this.cloneSnapshot(this.initialSnapshot ?? this.form.getRawValue());
|
|
7915
|
+
this.form.reset(snapshot);
|
|
7916
|
+
this.form.markAsPristine();
|
|
7917
|
+
this.form.markAsUntouched();
|
|
7918
|
+
this.form.updateValueAndValidity({ emitEvent: true });
|
|
6211
7919
|
this.isDirty$.next(false);
|
|
6212
7920
|
}
|
|
6213
7921
|
onControlTypeChange(next) {
|
|
@@ -6225,28 +7933,162 @@ class FieldMetadataEditorComponent {
|
|
|
6225
7933
|
const missingSharedProps = fieldSubmitProperties.filter((prop) => !propNames.has(prop.name));
|
|
6226
7934
|
return missingSharedProps.length ? [...props, ...missingSharedProps] : props;
|
|
6227
7935
|
}
|
|
7936
|
+
get isArrayEditor() {
|
|
7937
|
+
return this.controlType === FieldControlType.ARRAY_INPUT;
|
|
7938
|
+
}
|
|
7939
|
+
buildRenderedProps(props) {
|
|
7940
|
+
if (!this.isArrayEditor) {
|
|
7941
|
+
return props;
|
|
7942
|
+
}
|
|
7943
|
+
return (props || []).filter((prop) => prop?.name !== 'array.itemSchema.fields');
|
|
7944
|
+
}
|
|
7945
|
+
cloneSnapshot(value) {
|
|
7946
|
+
return this.cloneMetadataValue(value, new WeakMap());
|
|
7947
|
+
}
|
|
7948
|
+
cloneMetadataValue(value, seen) {
|
|
7949
|
+
if (!value || typeof value !== 'object') {
|
|
7950
|
+
return value;
|
|
7951
|
+
}
|
|
7952
|
+
if (seen.has(value)) {
|
|
7953
|
+
return seen.get(value);
|
|
7954
|
+
}
|
|
7955
|
+
if (value instanceof Date) {
|
|
7956
|
+
return new Date(value.getTime());
|
|
7957
|
+
}
|
|
7958
|
+
if (value instanceof RegExp) {
|
|
7959
|
+
return new RegExp(value.source, value.flags);
|
|
7960
|
+
}
|
|
7961
|
+
if (Array.isArray(value)) {
|
|
7962
|
+
const clone = [];
|
|
7963
|
+
seen.set(value, clone);
|
|
7964
|
+
clone.push(...value.map((entry) => this.cloneMetadataValue(entry, seen)));
|
|
7965
|
+
return clone;
|
|
7966
|
+
}
|
|
7967
|
+
if (value instanceof Map) {
|
|
7968
|
+
const clone = new Map();
|
|
7969
|
+
seen.set(value, clone);
|
|
7970
|
+
value.forEach((entry, key) => {
|
|
7971
|
+
clone.set(this.cloneMetadataValue(key, seen), this.cloneMetadataValue(entry, seen));
|
|
7972
|
+
});
|
|
7973
|
+
return clone;
|
|
7974
|
+
}
|
|
7975
|
+
if (value instanceof Set) {
|
|
7976
|
+
const clone = new Set();
|
|
7977
|
+
seen.set(value, clone);
|
|
7978
|
+
value.forEach((entry) => clone.add(this.cloneMetadataValue(entry, seen)));
|
|
7979
|
+
return clone;
|
|
7980
|
+
}
|
|
7981
|
+
const prototype = Object.getPrototypeOf(value);
|
|
7982
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
7983
|
+
return value;
|
|
7984
|
+
}
|
|
7985
|
+
const clone = {};
|
|
7986
|
+
seen.set(value, clone);
|
|
7987
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
7988
|
+
clone[key] = this.cloneMetadataValue(entry, seen);
|
|
7989
|
+
}
|
|
7990
|
+
return clone;
|
|
7991
|
+
}
|
|
7992
|
+
canEmitSettingsValue() {
|
|
7993
|
+
this.validateArrayItemSchemaFields();
|
|
7994
|
+
this.form.updateValueAndValidity({ emitEvent: true });
|
|
7995
|
+
const valid = this.form.valid;
|
|
7996
|
+
this.isValid$.next(valid);
|
|
7997
|
+
if (!valid) {
|
|
7998
|
+
this.form.markAllAsTouched();
|
|
7999
|
+
}
|
|
8000
|
+
return valid;
|
|
8001
|
+
}
|
|
8002
|
+
validateArrayItemSchemaFields() {
|
|
8003
|
+
if (!this.isArrayEditor) {
|
|
8004
|
+
return;
|
|
8005
|
+
}
|
|
8006
|
+
const control = this.form.get('array.itemSchema.fields');
|
|
8007
|
+
if (!control) {
|
|
8008
|
+
return;
|
|
8009
|
+
}
|
|
8010
|
+
const value = control.value;
|
|
8011
|
+
const fields = Array.isArray(value)
|
|
8012
|
+
? value
|
|
8013
|
+
: typeof value === 'string' && value.trim()
|
|
8014
|
+
? this.parseArrayItemFields(value)
|
|
8015
|
+
: [];
|
|
8016
|
+
const seen = new Set();
|
|
8017
|
+
const duplicated = new Set();
|
|
8018
|
+
let hasEmptyName = false;
|
|
8019
|
+
for (const field of fields) {
|
|
8020
|
+
const name = String(field?.name || '').trim();
|
|
8021
|
+
if (!name) {
|
|
8022
|
+
hasEmptyName = true;
|
|
8023
|
+
continue;
|
|
8024
|
+
}
|
|
8025
|
+
if (seen.has(name)) {
|
|
8026
|
+
duplicated.add(name);
|
|
8027
|
+
}
|
|
8028
|
+
seen.add(name);
|
|
8029
|
+
}
|
|
8030
|
+
this.setControlError(control, 'arrayFieldNameRequired', hasEmptyName);
|
|
8031
|
+
this.setControlError(control, 'arrayFieldNameDuplicated', duplicated.size ? { names: Array.from(duplicated) } : null);
|
|
8032
|
+
}
|
|
8033
|
+
parseArrayItemFields(value) {
|
|
8034
|
+
try {
|
|
8035
|
+
const parsed = JSON.parse(value);
|
|
8036
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
8037
|
+
}
|
|
8038
|
+
catch {
|
|
8039
|
+
return [];
|
|
8040
|
+
}
|
|
8041
|
+
}
|
|
8042
|
+
setControlError(control, key, value) {
|
|
8043
|
+
const errors = { ...(control.errors || {}) };
|
|
8044
|
+
const hasError = value !== null && value !== undefined && value !== false;
|
|
8045
|
+
if (hasError) {
|
|
8046
|
+
errors[key] = value === true ? true : value;
|
|
8047
|
+
}
|
|
8048
|
+
else {
|
|
8049
|
+
delete errors[key];
|
|
8050
|
+
}
|
|
8051
|
+
control.setErrors(Object.keys(errors).length ? errors : null);
|
|
8052
|
+
}
|
|
6228
8053
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: FieldMetadataEditorComponent, deps: [{ token: ConfigRegistryService }, { token: SchemaNormalizerService }, { token: DynamicFormFactoryService }, { token: ContextValidatorRegistryService }], target: i0.ɵɵFactoryTarget.Component });
|
|
6229
8054
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: FieldMetadataEditorComponent, isStandalone: true, selector: "praxis-field-metadata-editor", inputs: { controlType: "controlType", seed: "seed" }, outputs: { applied: "applied", cancel: "cancel" }, ngImport: i0, template: `
|
|
6230
8055
|
<div class="p-3" *ngIf="form">
|
|
6231
8056
|
<praxis-dynamic-editor-renderer
|
|
6232
|
-
[properties]="
|
|
8057
|
+
[properties]="renderedProps"
|
|
6233
8058
|
[form]="form"
|
|
6234
8059
|
></praxis-dynamic-editor-renderer>
|
|
8060
|
+
|
|
8061
|
+
<praxis-array-item-schema-fields-editor
|
|
8062
|
+
*ngIf="isArrayEditor"
|
|
8063
|
+
[control]="form.get('array.itemSchema.fields')"
|
|
8064
|
+
[arrayControl]="form.get('array')"
|
|
8065
|
+
></praxis-array-item-schema-fields-editor>
|
|
6235
8066
|
</div>
|
|
6236
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3
|
|
8067
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: DynamicEditorRendererComponent, selector: "praxis-dynamic-editor-renderer", inputs: ["properties", "form"] }, { kind: "component", type: ArrayItemSchemaFieldsEditorComponent, selector: "praxis-array-item-schema-fields-editor", inputs: ["control", "arrayControl"] }] });
|
|
6237
8068
|
}
|
|
6238
8069
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: FieldMetadataEditorComponent, decorators: [{
|
|
6239
8070
|
type: Component,
|
|
6240
8071
|
args: [{
|
|
6241
8072
|
selector: 'praxis-field-metadata-editor',
|
|
6242
8073
|
standalone: true,
|
|
6243
|
-
imports: [
|
|
8074
|
+
imports: [
|
|
8075
|
+
CommonModule,
|
|
8076
|
+
ReactiveFormsModule,
|
|
8077
|
+
DynamicEditorRendererComponent,
|
|
8078
|
+
ArrayItemSchemaFieldsEditorComponent,
|
|
8079
|
+
],
|
|
6244
8080
|
template: `
|
|
6245
8081
|
<div class="p-3" *ngIf="form">
|
|
6246
8082
|
<praxis-dynamic-editor-renderer
|
|
6247
|
-
[properties]="
|
|
8083
|
+
[properties]="renderedProps"
|
|
6248
8084
|
[form]="form"
|
|
6249
8085
|
></praxis-dynamic-editor-renderer>
|
|
8086
|
+
|
|
8087
|
+
<praxis-array-item-schema-fields-editor
|
|
8088
|
+
*ngIf="isArrayEditor"
|
|
8089
|
+
[control]="form.get('array.itemSchema.fields')"
|
|
8090
|
+
[arrayControl]="form.get('array')"
|
|
8091
|
+
></praxis-array-item-schema-fields-editor>
|
|
6250
8092
|
</div>
|
|
6251
8093
|
`,
|
|
6252
8094
|
}]
|
|
@@ -6267,15 +8109,19 @@ class CascadeRulesService {
|
|
|
6267
8109
|
return map;
|
|
6268
8110
|
}
|
|
6269
8111
|
hydrateRule(field) {
|
|
6270
|
-
const deps = field.dependencyFields
|
|
8112
|
+
const deps = (field.dependencyFields ??
|
|
8113
|
+
field.optionSource?.dependsOn);
|
|
6271
8114
|
if (!deps || deps.length === 0)
|
|
6272
8115
|
return null;
|
|
8116
|
+
const dependencyFilterMap = field.dependencyFilterMap ??
|
|
8117
|
+
field.optionSource?.dependencyFilterMap ??
|
|
8118
|
+
null;
|
|
6273
8119
|
const vm = {
|
|
6274
8120
|
targetField: field.name,
|
|
6275
8121
|
dependencyFields: [...deps],
|
|
6276
8122
|
enableDependencyCascade: field.enableDependencyCascade === false ? false : true,
|
|
6277
8123
|
resetOnDependentChange: !!field.resetOnDependentChange,
|
|
6278
|
-
dependencyFilterMap
|
|
8124
|
+
dependencyFilterMap,
|
|
6279
8125
|
dependencyValuePath: field.dependencyValuePath || null,
|
|
6280
8126
|
dependencyMergeStrategy: field.dependencyMergeStrategy || 'merge',
|
|
6281
8127
|
dependencyDebounceMs: field.dependencyDebounceMs != null
|
|
@@ -6956,7 +8802,7 @@ class CascadeManagerTabComponent {
|
|
|
6956
8802
|
</div>
|
|
6957
8803
|
</div>
|
|
6958
8804
|
</div>
|
|
6959
|
-
`, isInline: true, styles: [":host{display:block}.cm-root{display:grid;grid-template-columns:320px 1fr;gap:16px;min-height:420px}.cm-left{border-right:1px solid var(--md-sys-color-outline-variant);padding-right:8px}.cm-search{width:100%;margin-bottom:8px}.cm-right{padding-left:8px}.field-item{display:flex;align-items:center;justify-content:space-between;padding:8px 6px;cursor:pointer;border-radius:10px;transition:background .12s ease,border .12s ease;border:1px solid transparent}.field-item:hover{background:var(--md-sys-color-surface-container-high)}.field-item.selected{background:var(--md-sys-color-surface-container-highest);border-color:var(--md-sys-color-outline-variant)}.rule-badges{display:inline-flex;gap:6px}.rule-badge{font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600}.b-active{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.b-manual{background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.b-immediate{background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container)}.b-conflict{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.cm-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.rules-empty{opacity:.8;font-style:italic;padding:8px 0;color:var(--md-sys-color-on-surface-variant)}.rule-row{display:grid;grid-template-columns:1fr auto;align-items:center;padding:10px 0;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.rule-actions button{margin-left:4px}.banner{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container);padding:10px 12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;margin-bottom:10px;display:flex;gap:8px;align-items:center}.import-panel{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;padding:10px;margin-bottom:10px}.full{width:100%}.form-row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.form-grid{display:grid;gap:12px;padding:8px 0}.map-table{width:100%;border-collapse:collapse}.map-table th,.map-table td{padding:8px;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.kbd{font-family:monospace;background:var(--md-sys-color-surface-container-highest);padding:0 6px;border-radius:4px;color:var(--md-sys-color-on-surface-variant)}.panel-surface{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;box-shadow:var(--md-sys-elevation-level1, none);margin-bottom:12px}.panel-stack{display:grid;gap:12px;margin:12px 0}.stack-panel{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;box-shadow:var(--md-sys-elevation-level1, none)}.stack-panel .mat-expansion-panel-content{padding:0 16px 12px}.panel-header{display:flex;align-items:center;gap:8px}.preview-box{background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;padding:8px;color:var(--md-sys-color-on-surface)}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.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: i3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i5$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { 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", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i7.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i7.MatLabel, selector: "mat-label" }, { kind: "directive", type: i7.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "ngmodule", type: MatListModule }, { kind: "ngmodule", type: MatDividerModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8$1.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", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i8$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i9.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i5.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i5.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i5.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i11.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i11.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i11.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i12.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "directive", type: i12.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i13.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i13.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i13.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
|
|
8805
|
+
`, isInline: true, styles: [":host{display:block}.cm-root{display:grid;grid-template-columns:320px 1fr;gap:16px;min-height:420px}.cm-left{border-right:1px solid var(--md-sys-color-outline-variant);padding-right:8px}.cm-search{width:100%;margin-bottom:8px}.cm-right{padding-left:8px}.field-item{display:flex;align-items:center;justify-content:space-between;padding:8px 6px;cursor:pointer;border-radius:10px;transition:background .12s ease,border .12s ease;border:1px solid transparent}.field-item:hover{background:var(--md-sys-color-surface-container-high)}.field-item.selected{background:var(--md-sys-color-surface-container-highest);border-color:var(--md-sys-color-outline-variant)}.rule-badges{display:inline-flex;gap:6px}.rule-badge{font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600}.b-active{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.b-manual{background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.b-immediate{background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container)}.b-conflict{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.cm-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.rules-empty{opacity:.8;font-style:italic;padding:8px 0;color:var(--md-sys-color-on-surface-variant)}.rule-row{display:grid;grid-template-columns:1fr auto;align-items:center;padding:10px 0;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.rule-actions button{margin-left:4px}.banner{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container);padding:10px 12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;margin-bottom:10px;display:flex;gap:8px;align-items:center}.import-panel{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;padding:10px;margin-bottom:10px}.full{width:100%}.form-row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.form-grid{display:grid;gap:12px;padding:8px 0}.map-table{width:100%;border-collapse:collapse}.map-table th,.map-table td{padding:8px;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.kbd{font-family:monospace;background:var(--md-sys-color-surface-container-highest);padding:0 6px;border-radius:4px;color:var(--md-sys-color-on-surface-variant)}.panel-surface{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;box-shadow:var(--md-sys-elevation-level1, none);margin-bottom:12px}.panel-stack{display:grid;gap:12px;margin:12px 0}.stack-panel{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;box-shadow:var(--md-sys-elevation-level1, none)}.stack-panel .mat-expansion-panel-content{padding:0 16px 12px}.panel-header{display:flex;align-items:center;gap:8px}.preview-box{background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;padding:8px;color:var(--md-sys-color-on-surface)}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i5$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { 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", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i7.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i7.MatLabel, selector: "mat-label" }, { kind: "directive", type: i7.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "ngmodule", type: MatListModule }, { kind: "ngmodule", type: MatDividerModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8$1.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", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i8$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i9.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i5.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i5.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i5.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i11.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i11.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i11.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i12.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "directive", type: i12.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i13.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i13.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i13.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
|
|
6960
8806
|
}
|
|
6961
8807
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: CascadeManagerTabComponent, decorators: [{
|
|
6962
8808
|
type: Component,
|
|
@@ -7293,8 +9139,415 @@ const METADATA_EDITOR_AI_CAPABILITY_CATALOG = {
|
|
|
7293
9139
|
enums: {},
|
|
7294
9140
|
};
|
|
7295
9141
|
|
|
9142
|
+
const fieldMetadataPropertySchema = {
|
|
9143
|
+
type: 'object',
|
|
9144
|
+
required: ['path', 'value'],
|
|
9145
|
+
properties: {
|
|
9146
|
+
path: {
|
|
9147
|
+
enum: [
|
|
9148
|
+
'name',
|
|
9149
|
+
'label',
|
|
9150
|
+
'description',
|
|
9151
|
+
'controlType',
|
|
9152
|
+
'placeholder',
|
|
9153
|
+
'defaultValue',
|
|
9154
|
+
'group',
|
|
9155
|
+
'order',
|
|
9156
|
+
'required',
|
|
9157
|
+
'disabled',
|
|
9158
|
+
'readOnly',
|
|
9159
|
+
'hidden',
|
|
9160
|
+
'source',
|
|
9161
|
+
'transient',
|
|
9162
|
+
'submitPolicy',
|
|
9163
|
+
'hint',
|
|
9164
|
+
'helpText',
|
|
9165
|
+
'tooltip',
|
|
9166
|
+
'options',
|
|
9167
|
+
'optionSource',
|
|
9168
|
+
'validators',
|
|
9169
|
+
'conditionalRequired',
|
|
9170
|
+
'conditionalDisplay',
|
|
9171
|
+
'visibleIn',
|
|
9172
|
+
'ariaLabel',
|
|
9173
|
+
'ariaDescribedBy',
|
|
9174
|
+
],
|
|
9175
|
+
},
|
|
9176
|
+
value: {},
|
|
9177
|
+
merge: { type: 'boolean' },
|
|
9178
|
+
},
|
|
9179
|
+
};
|
|
9180
|
+
const optionSourceSchema = {
|
|
9181
|
+
type: 'object',
|
|
9182
|
+
minProperties: 1,
|
|
9183
|
+
properties: {
|
|
9184
|
+
kind: { enum: ['static', 'remote', 'resource', 'lookup'] },
|
|
9185
|
+
resource: { type: 'string' },
|
|
9186
|
+
endpoint: { type: 'string' },
|
|
9187
|
+
valueField: { type: 'string' },
|
|
9188
|
+
labelField: { type: 'string' },
|
|
9189
|
+
dependsOn: { type: 'string' },
|
|
9190
|
+
dependencyFilterMap: { type: 'object' },
|
|
9191
|
+
options: { type: 'array' },
|
|
9192
|
+
},
|
|
9193
|
+
};
|
|
9194
|
+
const cascadeSchema = {
|
|
9195
|
+
type: 'object',
|
|
9196
|
+
required: ['dependentField'],
|
|
9197
|
+
properties: {
|
|
9198
|
+
dependentField: { type: 'string' },
|
|
9199
|
+
sourceField: { type: 'string' },
|
|
9200
|
+
strategy: { enum: ['replace', 'merge'] },
|
|
9201
|
+
debounceMs: { type: 'number' },
|
|
9202
|
+
loadMode: { enum: ['immediate', 'manual', 'respectLoadOn'] },
|
|
9203
|
+
dependencyFilterMap: { type: 'object' },
|
|
9204
|
+
optionSource: {
|
|
9205
|
+
type: 'object',
|
|
9206
|
+
properties: {
|
|
9207
|
+
kind: { enum: ['static', 'remote', 'resource', 'lookup'] },
|
|
9208
|
+
resource: { type: 'string' },
|
|
9209
|
+
endpoint: { type: 'string' },
|
|
9210
|
+
dependsOn: { type: 'string' },
|
|
9211
|
+
dependencyFilterMap: { type: 'object' },
|
|
9212
|
+
},
|
|
9213
|
+
},
|
|
9214
|
+
},
|
|
9215
|
+
};
|
|
9216
|
+
const PRAXIS_METADATA_EDITOR_AUTHORING_MANIFEST = {
|
|
9217
|
+
schemaVersion: '1.0.0',
|
|
9218
|
+
componentId: 'praxis-metadata-editor',
|
|
9219
|
+
ownerPackage: '@praxisui/metadata-editor',
|
|
9220
|
+
configSchemaId: 'FieldMetadata',
|
|
9221
|
+
manifestVersion: '1.0.0',
|
|
9222
|
+
runtimeInputs: [
|
|
9223
|
+
{ name: 'controlType', type: 'FieldControlType', description: 'Canonical control type used to resolve editor property coverage.' },
|
|
9224
|
+
{ name: 'seed', type: 'Partial<FieldMetadata | FieldDefinition>', description: 'Initial canonical field metadata used to hydrate the editor.' },
|
|
9225
|
+
{ name: 'fields', type: 'Array<FieldMetadata | FieldDefinition>', description: 'Available fields used by cascade authoring and context validation.' },
|
|
9226
|
+
{ name: 'properties', type: 'EditorProperty[]', description: 'Renderer property catalog resolved from ConfigRegistryService.' },
|
|
9227
|
+
{ name: 'form', type: 'FormGroup', description: 'Dynamic form generated from canonical editor properties by DynamicFormFactoryService.' },
|
|
9228
|
+
],
|
|
9229
|
+
editableTargets: [
|
|
9230
|
+
{ kind: 'fieldMetadata', resolver: 'field-metadata-json-path', description: 'Canonical FieldMetadata root and property paths.' },
|
|
9231
|
+
{ kind: 'controlType', resolver: 'dynamic-fields-control-type-discovery', description: 'Control type selected from dynamic-fields discovery and editor config registry.' },
|
|
9232
|
+
{ kind: 'optionSource', resolver: 'field-metadata-option-source', description: 'Canonical option source including backend x-ui option source fields where applicable.' },
|
|
9233
|
+
{ kind: 'cascade', resolver: 'metadata-editor-cascade-rules', description: 'Cascade dependencies and dependency filter mappings edited through CascadeManagerTab.' },
|
|
9234
|
+
{ kind: 'renderer', resolver: 'metadata-editor-renderer-property', description: 'Dynamic editor renderer property coverage and editor component mapping.' },
|
|
9235
|
+
{ kind: 'validation', resolver: 'field-metadata-validation-rules', description: 'Field validation rules and contextual validators.' },
|
|
9236
|
+
{ kind: 'contextHint', resolver: 'metadata-editor-context-hints', description: 'Authoring hints, help text and contextual guidance for metadata editors.' },
|
|
9237
|
+
{ kind: 'normalization', resolver: 'metadata-editor-schema-normalizer', description: 'Schema normalization and seed hydration rules.' },
|
|
9238
|
+
],
|
|
9239
|
+
operations: [
|
|
9240
|
+
{
|
|
9241
|
+
operationId: 'fieldMetadata.property.set',
|
|
9242
|
+
title: 'Set FieldMetadata property',
|
|
9243
|
+
scope: 'fieldMetadataPath',
|
|
9244
|
+
targetKind: 'fieldMetadata',
|
|
9245
|
+
target: { kind: 'fieldMetadata', resolver: 'field-metadata-json-path', ambiguityPolicy: 'fail', required: true },
|
|
9246
|
+
inputSchema: fieldMetadataPropertySchema,
|
|
9247
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-field-property-set', handlerContract: {
|
|
9248
|
+
reads: ['fieldMetadata', 'fieldMetadataPath', 'SchemaNormalizerService'],
|
|
9249
|
+
writes: ['fieldMetadata'],
|
|
9250
|
+
identityKeys: ['field.name', 'path'],
|
|
9251
|
+
inputSchema: fieldMetadataPropertySchema,
|
|
9252
|
+
failureModes: ['unknown-field-metadata-path', 'non-canonical-shape', 'readonly-field', 'normalization-failed'],
|
|
9253
|
+
description: 'Sets a canonical FieldMetadata property through a governed metadata path and re-runs schema normalization.',
|
|
9254
|
+
} }],
|
|
9255
|
+
destructive: false,
|
|
9256
|
+
requiresConfirmation: false,
|
|
9257
|
+
validators: ['field-metadata-shape-canonical', 'field-path-supported-by-editor', 'metadata-round-trip'],
|
|
9258
|
+
affectedPaths: ['fieldMetadata'],
|
|
9259
|
+
submissionImpact: 'affects-schema-backed-data',
|
|
9260
|
+
preconditions: ['field-metadata-loaded'],
|
|
9261
|
+
},
|
|
9262
|
+
{
|
|
9263
|
+
operationId: 'controlType.set',
|
|
9264
|
+
title: 'Set control type',
|
|
9265
|
+
scope: 'controlType',
|
|
9266
|
+
targetKind: 'controlType',
|
|
9267
|
+
target: { kind: 'controlType', resolver: 'dynamic-fields-control-type-discovery', ambiguityPolicy: 'fail', required: true },
|
|
9268
|
+
inputSchema: {
|
|
9269
|
+
type: 'object',
|
|
9270
|
+
required: ['controlType'],
|
|
9271
|
+
properties: {
|
|
9272
|
+
controlType: { type: 'string' },
|
|
9273
|
+
preserveCompatibleProperties: { type: 'boolean' },
|
|
9274
|
+
},
|
|
9275
|
+
},
|
|
9276
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-control-type-set', handlerContract: {
|
|
9277
|
+
reads: ['FieldControlType', 'ConfigRegistryService', 'dynamic-fields-discovery', 'fieldMetadata'],
|
|
9278
|
+
writes: ['fieldMetadata.controlType', 'properties', 'normalizedSeed'],
|
|
9279
|
+
identityKeys: ['field.name', 'controlType'],
|
|
9280
|
+
inputSchema: { type: 'object', required: ['controlType'], properties: { controlType: { type: 'string' }, preserveCompatibleProperties: { type: 'boolean' } } },
|
|
9281
|
+
failureModes: ['control-type-not-discovered', 'editor-coverage-missing', 'incompatible-metadata-preservation'],
|
|
9282
|
+
description: 'Changes control type only when dynamic-fields discovery and metadata-editor config registry both expose renderer/editor coverage.',
|
|
9283
|
+
} }],
|
|
9284
|
+
destructive: false,
|
|
9285
|
+
requiresConfirmation: false,
|
|
9286
|
+
validators: ['control-type-exists-in-discovery', 'editor-coverage-exists', 'metadata-round-trip'],
|
|
9287
|
+
affectedPaths: ['fieldMetadata.controlType', 'properties', 'normalizedSeed'],
|
|
9288
|
+
submissionImpact: 'affects-schema-backed-data',
|
|
9289
|
+
preconditions: ['dynamic-fields-discovery-loaded'],
|
|
9290
|
+
},
|
|
9291
|
+
{
|
|
9292
|
+
operationId: 'optionSource.configure',
|
|
9293
|
+
title: 'Configure option source',
|
|
9294
|
+
scope: 'dataBinding',
|
|
9295
|
+
targetKind: 'optionSource',
|
|
9296
|
+
target: { kind: 'optionSource', resolver: 'field-metadata-option-source', ambiguityPolicy: 'fail', required: false },
|
|
9297
|
+
inputSchema: optionSourceSchema,
|
|
9298
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-option-source-configure', handlerContract: {
|
|
9299
|
+
reads: ['fieldMetadata.optionSource', 'backend.x-ui.optionSource', 'SchemaNormalizerService'],
|
|
9300
|
+
writes: ['fieldMetadata.optionSource'],
|
|
9301
|
+
identityKeys: ['field.name', 'optionSource.kind', 'optionSource.resource'],
|
|
9302
|
+
inputSchema: optionSourceSchema,
|
|
9303
|
+
failureModes: ['invalid-option-source-kind', 'remote-source-missing-governed-resource', 'dependency-filter-map-invalid'],
|
|
9304
|
+
description: 'Configures canonical optionSource fields while preserving backend x-ui dependsOn and dependencyFilterMap semantics.',
|
|
9305
|
+
} }],
|
|
9306
|
+
destructive: false,
|
|
9307
|
+
requiresConfirmation: false,
|
|
9308
|
+
validators: ['option-source-shape-canonical', 'remote-option-source-governed', 'cascade-backend-shape-preserved', 'metadata-round-trip'],
|
|
9309
|
+
affectedPaths: ['fieldMetadata.optionSource'],
|
|
9310
|
+
submissionImpact: 'affects-remote-binding',
|
|
9311
|
+
preconditions: ['field-metadata-loaded'],
|
|
9312
|
+
},
|
|
9313
|
+
{
|
|
9314
|
+
operationId: 'cascade.configure',
|
|
9315
|
+
title: 'Configure cascade rule',
|
|
9316
|
+
scope: 'interaction',
|
|
9317
|
+
targetKind: 'cascade',
|
|
9318
|
+
target: { kind: 'cascade', resolver: 'metadata-editor-cascade-rules', ambiguityPolicy: 'fail', required: false },
|
|
9319
|
+
inputSchema: cascadeSchema,
|
|
9320
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-cascade-configure', handlerContract: {
|
|
9321
|
+
reads: ['CascadeManagerTabComponent', 'CascadeRulesService', 'fields', 'fieldMetadata.optionSource', 'fieldMetadata.dependencyFields', 'fieldMetadata.dependencyFilterMap'],
|
|
9322
|
+
writes: ['fieldMetadata.dependencyFields', 'fieldMetadata.enableDependencyCascade', 'fieldMetadata.resetOnDependentChange', 'fieldMetadata.dependencyFilterMap', 'fieldMetadata.dependencyValuePath', 'fieldMetadata.dependencyMergeStrategy', 'fieldMetadata.dependencyDebounceMs', 'fieldMetadata.dependencyLoadOnChange', 'fieldMetadata.optionSource.dependsOn', 'fieldMetadata.optionSource.dependencyFilterMap'],
|
|
9323
|
+
identityKeys: ['dependentField', 'sourceField'],
|
|
9324
|
+
inputSchema: cascadeSchema,
|
|
9325
|
+
failureModes: ['dependent-field-not-found', 'source-field-not-found', 'cascade-cycle-detected', 'dependency-filter-map-invalid'],
|
|
9326
|
+
description: 'Applies cascade dependencies through CascadeRulesService using the metadata-editor save contract while preserving backend x-ui optionSource dependency semantics during hydration.',
|
|
9327
|
+
} }],
|
|
9328
|
+
destructive: false,
|
|
9329
|
+
requiresConfirmation: false,
|
|
9330
|
+
validators: ['cascade-fields-exist', 'cascade-cycle-free', 'cascade-backend-shape-preserved', 'option-source-shape-canonical', 'remote-option-source-governed', 'metadata-round-trip'],
|
|
9331
|
+
affectedPaths: ['fieldMetadata.dependencyFields', 'fieldMetadata.enableDependencyCascade', 'fieldMetadata.resetOnDependentChange', 'fieldMetadata.dependencyFilterMap', 'fieldMetadata.dependencyValuePath', 'fieldMetadata.dependencyMergeStrategy', 'fieldMetadata.dependencyDebounceMs', 'fieldMetadata.dependencyLoadOnChange', 'fieldMetadata.optionSource.dependsOn', 'fieldMetadata.optionSource.dependencyFilterMap'],
|
|
9332
|
+
submissionImpact: 'affects-remote-binding',
|
|
9333
|
+
preconditions: ['field-list-loaded'],
|
|
9334
|
+
},
|
|
9335
|
+
{
|
|
9336
|
+
operationId: 'renderer.configure',
|
|
9337
|
+
title: 'Configure renderer coverage',
|
|
9338
|
+
scope: 'editorCoverage',
|
|
9339
|
+
targetKind: 'renderer',
|
|
9340
|
+
target: { kind: 'renderer', resolver: 'metadata-editor-renderer-property', ambiguityPolicy: 'fail', required: true },
|
|
9341
|
+
inputSchema: {
|
|
9342
|
+
type: 'object',
|
|
9343
|
+
required: ['propertyName', 'editorType'],
|
|
9344
|
+
properties: {
|
|
9345
|
+
propertyName: { type: 'string' },
|
|
9346
|
+
editorType: { type: 'string' },
|
|
9347
|
+
group: { type: 'string' },
|
|
9348
|
+
required: { type: 'boolean' },
|
|
9349
|
+
options: { type: 'array' },
|
|
9350
|
+
},
|
|
9351
|
+
},
|
|
9352
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-renderer-configure', handlerContract: {
|
|
9353
|
+
reads: ['DynamicEditorRendererComponent', 'EditorComponentRegistryService', 'ConfigRegistryService', 'EditorProperty'],
|
|
9354
|
+
writes: ['properties', 'editorCoverage'],
|
|
9355
|
+
identityKeys: ['controlType', 'propertyName'],
|
|
9356
|
+
inputSchema: { type: 'object', required: ['propertyName', 'editorType'], properties: { propertyName: { type: 'string' }, editorType: { type: 'string' }, group: { type: 'string' } } },
|
|
9357
|
+
failureModes: ['editor-type-not-registered', 'property-not-covered', 'json-only-coverage-not-accepted'],
|
|
9358
|
+
description: 'Configures visual renderer coverage for a metadata property and rejects JSON-only support for required visual authoring.',
|
|
9359
|
+
} }],
|
|
9360
|
+
destructive: false,
|
|
9361
|
+
requiresConfirmation: false,
|
|
9362
|
+
validators: ['renderer-editor-type-registered', 'visual-editor-coverage-required', 'editor-coverage-exists', 'metadata-round-trip'],
|
|
9363
|
+
affectedPaths: ['properties', 'editorCoverage'],
|
|
9364
|
+
submissionImpact: 'config-only',
|
|
9365
|
+
preconditions: ['config-registry-loaded'],
|
|
9366
|
+
},
|
|
9367
|
+
{
|
|
9368
|
+
operationId: 'validationRule.add',
|
|
9369
|
+
title: 'Add validation rule',
|
|
9370
|
+
scope: 'fieldMetadataPath',
|
|
9371
|
+
targetKind: 'validation',
|
|
9372
|
+
target: { kind: 'validation', resolver: 'field-metadata-validation-rules', ambiguityPolicy: 'fail', required: false },
|
|
9373
|
+
inputSchema: {
|
|
9374
|
+
type: 'object',
|
|
9375
|
+
required: ['rule'],
|
|
9376
|
+
properties: {
|
|
9377
|
+
rule: { type: 'object' },
|
|
9378
|
+
message: { type: 'string' },
|
|
9379
|
+
contextValidatorId: { type: 'string' },
|
|
9380
|
+
},
|
|
9381
|
+
},
|
|
9382
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-validation-rule-add', handlerContract: {
|
|
9383
|
+
reads: ['fieldMetadata.validators', 'ContextValidatorRegistryService', 'DynamicFormFactoryService'],
|
|
9384
|
+
writes: ['fieldMetadata.validators'],
|
|
9385
|
+
identityKeys: ['field.name', 'rule.type', 'contextValidatorId'],
|
|
9386
|
+
inputSchema: { type: 'object', required: ['rule'], properties: { rule: { type: 'object' }, message: { type: 'string' }, contextValidatorId: { type: 'string' } } },
|
|
9387
|
+
failureModes: ['validation-rule-invalid', 'context-validator-not-registered', 'duplicate-validation-rule'],
|
|
9388
|
+
description: 'Adds a canonical validation rule and verifies contextual validator availability before persistence.',
|
|
9389
|
+
} }],
|
|
9390
|
+
destructive: false,
|
|
9391
|
+
requiresConfirmation: false,
|
|
9392
|
+
validators: ['validation-rule-canonical', 'context-validator-registered', 'metadata-round-trip'],
|
|
9393
|
+
affectedPaths: ['fieldMetadata.validators'],
|
|
9394
|
+
submissionImpact: 'affects-submission',
|
|
9395
|
+
preconditions: ['field-metadata-loaded'],
|
|
9396
|
+
},
|
|
9397
|
+
{
|
|
9398
|
+
operationId: 'contextHint.set',
|
|
9399
|
+
title: 'Set metadata context hint',
|
|
9400
|
+
scope: 'meta',
|
|
9401
|
+
targetKind: 'contextHint',
|
|
9402
|
+
target: { kind: 'contextHint', resolver: 'metadata-editor-context-hints', ambiguityPolicy: 'fail', required: false },
|
|
9403
|
+
inputSchema: {
|
|
9404
|
+
type: 'object',
|
|
9405
|
+
required: ['hintPath', 'value'],
|
|
9406
|
+
properties: {
|
|
9407
|
+
hintPath: { enum: ['hint', 'helpText', 'description', 'tooltip', 'ariaLabel', 'ariaDescribedBy'] },
|
|
9408
|
+
value: {},
|
|
9409
|
+
localeKey: { type: 'string' },
|
|
9410
|
+
},
|
|
9411
|
+
},
|
|
9412
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-context-hint-set', handlerContract: {
|
|
9413
|
+
reads: ['fieldMetadata.hint', 'fieldMetadata.helpText', 'fieldMetadata.description', 'fieldMetadata.tooltip', 'fieldMetadata.ariaLabel', 'fieldMetadata.ariaDescribedBy', 'metadata-editor-i18n'],
|
|
9414
|
+
writes: ['fieldMetadata.hint', 'fieldMetadata.helpText', 'fieldMetadata.description', 'fieldMetadata.tooltip', 'fieldMetadata.ariaLabel', 'fieldMetadata.ariaDescribedBy'],
|
|
9415
|
+
identityKeys: ['field.name', 'hintPath'],
|
|
9416
|
+
inputSchema: { type: 'object', required: ['hintPath', 'value'], properties: { hintPath: { enum: ['hint', 'helpText', 'description', 'tooltip', 'ariaLabel', 'ariaDescribedBy'] }, value: {}, localeKey: { type: 'string' } } },
|
|
9417
|
+
failureModes: ['hint-path-not-canonical', 'hint-value-not-serializable', 'i18n-key-missing'],
|
|
9418
|
+
description: 'Sets structured FieldMetadata hint/help/accessibility text through canonical metadata paths and metadata-editor i18n constraints.',
|
|
9419
|
+
} }],
|
|
9420
|
+
destructive: false,
|
|
9421
|
+
requiresConfirmation: false,
|
|
9422
|
+
validators: ['context-hint-shape-canonical', 'context-hint-i18n-compatible', 'metadata-round-trip'],
|
|
9423
|
+
affectedPaths: ['fieldMetadata.hint', 'fieldMetadata.helpText', 'fieldMetadata.description', 'fieldMetadata.tooltip', 'fieldMetadata.ariaLabel', 'fieldMetadata.ariaDescribedBy'],
|
|
9424
|
+
submissionImpact: 'visual-only',
|
|
9425
|
+
preconditions: ['field-metadata-loaded'],
|
|
9426
|
+
},
|
|
9427
|
+
{
|
|
9428
|
+
operationId: 'normalization.apply',
|
|
9429
|
+
title: 'Apply schema normalization',
|
|
9430
|
+
scope: 'runtimeCoverage',
|
|
9431
|
+
targetKind: 'normalization',
|
|
9432
|
+
target: { kind: 'normalization', resolver: 'metadata-editor-schema-normalizer', ambiguityPolicy: 'fail', required: false },
|
|
9433
|
+
inputSchema: {
|
|
9434
|
+
type: 'object',
|
|
9435
|
+
properties: {
|
|
9436
|
+
mode: { enum: ['hydrate-seed', 'coerce-types', 'apply-defaults', 'preserve-advanced-properties'] },
|
|
9437
|
+
preserveUnknownCanonicalFields: { type: 'boolean' },
|
|
9438
|
+
},
|
|
9439
|
+
},
|
|
9440
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-normalization-apply', handlerContract: {
|
|
9441
|
+
reads: ['SchemaNormalizerService', 'fieldMetadata', 'properties', 'DynamicFormFactoryService'],
|
|
9442
|
+
writes: ['normalizedSeed', 'form', 'fieldMetadata'],
|
|
9443
|
+
identityKeys: ['field.name', 'controlType', 'mode'],
|
|
9444
|
+
inputSchema: { type: 'object', properties: { mode: { enum: ['hydrate-seed', 'coerce-types', 'apply-defaults', 'preserve-advanced-properties'] }, preserveUnknownCanonicalFields: { type: 'boolean' } } },
|
|
9445
|
+
failureModes: ['normalizer-missing', 'type-coercion-failed', 'advanced-property-lost', 'runtime-editor-drift'],
|
|
9446
|
+
description: 'Runs metadata-editor schema normalization while preserving canonical advanced FieldMetadata properties and editor/runtime round-trip.',
|
|
9447
|
+
} }],
|
|
9448
|
+
destructive: false,
|
|
9449
|
+
requiresConfirmation: false,
|
|
9450
|
+
validators: ['normalization-preserves-canonical-fields', 'runtime-editor-round-trip', 'metadata-round-trip'],
|
|
9451
|
+
affectedPaths: ['normalizedSeed', 'form', 'fieldMetadata'],
|
|
9452
|
+
submissionImpact: 'config-only',
|
|
9453
|
+
preconditions: ['field-metadata-loaded', 'editor-properties-loaded'],
|
|
9454
|
+
},
|
|
9455
|
+
],
|
|
9456
|
+
validators: [
|
|
9457
|
+
{ validatorId: 'field-metadata-shape-canonical', level: 'error', code: 'METADATA_FIELD_SHAPE_CANONICAL', description: 'Edited metadata must remain compatible with canonical FieldMetadata.' },
|
|
9458
|
+
{ validatorId: 'field-path-supported-by-editor', level: 'error', code: 'METADATA_FIELD_PATH_SUPPORTED', description: 'Edited paths must be supported by visual editor coverage or explicitly delegated.' },
|
|
9459
|
+
{ validatorId: 'control-type-exists-in-discovery', level: 'error', code: 'METADATA_CONTROL_TYPE_DISCOVERED', description: 'Control type must exist in dynamic-fields discovery.' },
|
|
9460
|
+
{ validatorId: 'editor-coverage-exists', level: 'error', code: 'METADATA_EDITOR_COVERAGE_EXISTS', description: 'Control type and property must have metadata-editor coverage.' },
|
|
9461
|
+
{ validatorId: 'option-source-shape-canonical', level: 'error', code: 'METADATA_OPTION_SOURCE_CANONICAL', description: 'Option source must use canonical FieldMetadata/x-ui shape.' },
|
|
9462
|
+
{ validatorId: 'remote-option-source-governed', level: 'error', code: 'METADATA_REMOTE_OPTION_SOURCE_GOVERNED', description: 'Remote option sources must resolve through governed resource or endpoint metadata.' },
|
|
9463
|
+
{ validatorId: 'cascade-fields-exist', level: 'error', code: 'METADATA_CASCADE_FIELDS_EXIST', description: 'Cascade source and dependent fields must exist.' },
|
|
9464
|
+
{ validatorId: 'cascade-cycle-free', level: 'error', code: 'METADATA_CASCADE_CYCLE_FREE', description: 'Cascade dependencies must not create cycles.' },
|
|
9465
|
+
{ validatorId: 'cascade-backend-shape-preserved', level: 'error', code: 'METADATA_CASCADE_BACKEND_SHAPE_PRESERVED', description: 'Cascade config must preserve x-ui optionSource dependsOn and dependencyFilterMap semantics.' },
|
|
9466
|
+
{ validatorId: 'renderer-editor-type-registered', level: 'error', code: 'METADATA_RENDERER_TYPE_REGISTERED', description: 'Renderer editor type must be registered.' },
|
|
9467
|
+
{ validatorId: 'visual-editor-coverage-required', level: 'error', code: 'METADATA_VISUAL_COVERAGE_REQUIRED', description: 'JSON-only support is not accepted for required visual authoring coverage.' },
|
|
9468
|
+
{ validatorId: 'validation-rule-canonical', level: 'error', code: 'METADATA_VALIDATION_RULE_CANONICAL', description: 'Validation rule must use canonical metadata shape.' },
|
|
9469
|
+
{ validatorId: 'context-validator-registered', level: 'error', code: 'METADATA_CONTEXT_VALIDATOR_REGISTERED', description: 'Contextual validator must be registered before use.' },
|
|
9470
|
+
{ validatorId: 'context-hint-shape-canonical', level: 'error', code: 'METADATA_CONTEXT_HINT_CANONICAL', description: 'Context hints must remain structured metadata, not free-form host-only strings.' },
|
|
9471
|
+
{ validatorId: 'context-hint-i18n-compatible', level: 'warning', code: 'METADATA_CONTEXT_HINT_I18N_COMPATIBLE', description: 'Authoring hint text must remain compatible with metadata-editor i18n.' },
|
|
9472
|
+
{ validatorId: 'normalization-preserves-canonical-fields', level: 'error', code: 'METADATA_NORMALIZATION_PRESERVES_FIELDS', description: 'Normalization must not drop canonical advanced FieldMetadata properties.' },
|
|
9473
|
+
{ validatorId: 'runtime-editor-round-trip', level: 'error', code: 'METADATA_RUNTIME_EDITOR_ROUND_TRIP', description: 'Runtime and editor must consume the same normalized metadata shape.' },
|
|
9474
|
+
{ validatorId: 'metadata-round-trip', level: 'error', code: 'METADATA_ROUND_TRIP', description: 'Open, edit, apply/save, reopen and runtime consume must preserve metadata.' },
|
|
9475
|
+
],
|
|
9476
|
+
roundTripRequirements: [
|
|
9477
|
+
'FieldMetadata is the canonical edited document shape; backend x-ui-derived metadata must not be rewritten into host-only aliases.',
|
|
9478
|
+
'Control type changes require both dynamic-fields discovery and metadata-editor visual coverage.',
|
|
9479
|
+
'Option source cascades preserve x-ui optionSource.dependsOn and optionSource.dependencyFilterMap semantics.',
|
|
9480
|
+
'JSON-only support is incomplete when visual editor coverage is required.',
|
|
9481
|
+
'Schema normalization must preserve advanced canonical properties through open, edit, apply/save, reopen and runtime consumption.',
|
|
9482
|
+
],
|
|
9483
|
+
examples: [
|
|
9484
|
+
{ id: 'set-label', request: 'Set this field label to Customer name.', operationId: 'fieldMetadata.property.set', params: { path: 'label', value: 'Customer name' }, isPositive: true },
|
|
9485
|
+
{ id: 'set-control-type', request: 'Change this field to a select control.', operationId: 'controlType.set', params: { controlType: 'select', preserveCompatibleProperties: true }, isPositive: true },
|
|
9486
|
+
{ id: 'configure-remote-options', request: 'Load options from the customers resource using id and name.', operationId: 'optionSource.configure', params: { kind: 'resource', resource: 'customers', valueField: 'id', labelField: 'name' }, isPositive: true },
|
|
9487
|
+
{ id: 'configure-cascade', request: 'Filter city options when state changes.', operationId: 'cascade.configure', params: { dependentField: 'city', sourceField: 'state', dependencyFilterMap: { stateId: 'state.id' } }, isPositive: true },
|
|
9488
|
+
{ id: 'configure-renderer', request: 'Expose placeholder in the visual editor as a text field.', operationId: 'renderer.configure', params: { propertyName: 'placeholder', editorType: 'text', group: 'Presentation' }, isPositive: true },
|
|
9489
|
+
{ id: 'add-required-validation', request: 'Make this field required with a validation message.', operationId: 'validationRule.add', params: { rule: { type: 'required' }, message: 'Required field' }, isPositive: true },
|
|
9490
|
+
{ id: 'set-context-hint', request: 'Add a contextual hint for sales users.', operationId: 'contextHint.set', params: { hintPath: 'helpText', value: 'Use the legal customer name.' }, isPositive: true },
|
|
9491
|
+
{ id: 'normalize-seed', request: 'Normalize this imported field metadata while preserving advanced properties.', operationId: 'normalization.apply', params: { mode: 'preserve-advanced-properties', preserveUnknownCanonicalFields: true }, isPositive: true },
|
|
9492
|
+
{ id: 'reject-unknown-control', request: 'Use an unknown control type named magic-picker.', operationId: 'controlType.set', params: { controlType: 'magic-picker' }, isPositive: false },
|
|
9493
|
+
{ id: 'reject-json-only-coverage', request: 'Support this field only by editing raw JSON.', operationId: 'renderer.configure', params: { propertyName: 'optionSource', editorType: 'json' }, isPositive: false },
|
|
9494
|
+
{ id: 'reject-cascade-cycle', request: 'Make field A depend on B and B depend on A.', operationId: 'cascade.configure', params: { dependentField: 'a', sourceField: 'b' }, isPositive: false },
|
|
9495
|
+
{ id: 'reject-host-only-option-source', request: 'Save a host-only URL string as the option source.', operationId: 'optionSource.configure', params: { kind: 'remote', endpoint: 'local-only://options' }, isPositive: false },
|
|
9496
|
+
],
|
|
9497
|
+
};
|
|
9498
|
+
|
|
9499
|
+
/** Metadata for Praxis Metadata Editor component */
|
|
9500
|
+
const PRAXIS_METADATA_EDITOR_COMPONENT_METADATA = {
|
|
9501
|
+
id: 'praxis-metadata-editor',
|
|
9502
|
+
selector: 'praxis-field-metadata-editor',
|
|
9503
|
+
component: FieldMetadataEditorComponent,
|
|
9504
|
+
friendlyName: 'Praxis Metadata Editor',
|
|
9505
|
+
description: 'Editor for field metadata and schema helpers.',
|
|
9506
|
+
inputs: [
|
|
9507
|
+
{
|
|
9508
|
+
name: 'controlType',
|
|
9509
|
+
type: 'FieldControlType',
|
|
9510
|
+
label: 'Tipo de controle',
|
|
9511
|
+
description: 'Tipo de controle do campo alvo.',
|
|
9512
|
+
},
|
|
9513
|
+
{
|
|
9514
|
+
name: 'seed',
|
|
9515
|
+
type: 'Partial<FieldDefinition>',
|
|
9516
|
+
label: 'Seed',
|
|
9517
|
+
description: 'Valores iniciais do FieldDefinition para hidratar o editor.',
|
|
9518
|
+
},
|
|
9519
|
+
],
|
|
9520
|
+
outputs: [
|
|
9521
|
+
{
|
|
9522
|
+
name: 'applied',
|
|
9523
|
+
type: 'Partial<FieldDefinition>',
|
|
9524
|
+
label: 'Aplicado',
|
|
9525
|
+
description: 'Disparado quando o patch de metadata e aplicado.',
|
|
9526
|
+
},
|
|
9527
|
+
{
|
|
9528
|
+
name: 'cancel',
|
|
9529
|
+
type: 'void',
|
|
9530
|
+
label: 'Cancelar',
|
|
9531
|
+
description: 'Disparado quando o usuario cancela a edicao.',
|
|
9532
|
+
},
|
|
9533
|
+
],
|
|
9534
|
+
tags: ['widget', 'metadata', 'configurable'],
|
|
9535
|
+
lib: '@praxisui/metadata-editor',
|
|
9536
|
+
};
|
|
9537
|
+
/** Provider para auto-registrar metadados do componente Metadata Editor. */
|
|
9538
|
+
function providePraxisMetadataEditorMetadata() {
|
|
9539
|
+
return {
|
|
9540
|
+
provide: ENVIRONMENT_INITIALIZER,
|
|
9541
|
+
multi: true,
|
|
9542
|
+
useFactory: (registry) => () => {
|
|
9543
|
+
registry.register(PRAXIS_METADATA_EDITOR_COMPONENT_METADATA);
|
|
9544
|
+
},
|
|
9545
|
+
deps: [ComponentMetadataRegistry],
|
|
9546
|
+
};
|
|
9547
|
+
}
|
|
9548
|
+
|
|
7296
9549
|
/**
|
|
7297
9550
|
* Generated bundle index. Do not edit.
|
|
7298
9551
|
*/
|
|
7299
9552
|
|
|
7300
|
-
export { CascadeManagerTabComponent, CascadeRulesService, ConfigRegistryService, ContextValidatorRegistryService, DynamicEditorRendererComponent, DynamicFormFactoryService, EditorComponentRegistryService, FieldMetadataEditorComponent, METADATA_EDITOR_AI_CAPABILITIES, METADATA_EDITOR_AI_CAPABILITY_CATALOG, SchemaNormalizerService };
|
|
9553
|
+
export { CascadeManagerTabComponent, CascadeRulesService, ConfigRegistryService, ContextValidatorRegistryService, DynamicEditorRendererComponent, DynamicFormFactoryService, EditorComponentRegistryService, FieldMetadataEditorComponent, METADATA_EDITOR_AI_CAPABILITIES, METADATA_EDITOR_AI_CAPABILITY_CATALOG, PRAXIS_METADATA_EDITOR_AUTHORING_MANIFEST, PRAXIS_METADATA_EDITOR_COMPONENT_METADATA, SchemaNormalizerService, providePraxisMetadataEditorMetadata };
|