@praxisui/page-builder 3.0.0-beta.3 → 3.0.0-beta.5
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.
|
@@ -19,7 +19,7 @@ import * as i4 from '@angular/material/input';
|
|
|
19
19
|
import { MatInputModule } from '@angular/material/input';
|
|
20
20
|
import * as i1$2 from '@angular/forms';
|
|
21
21
|
import { FormsModule, FormGroup, FormArray, FormControl, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
22
|
-
import * as
|
|
22
|
+
import * as i6 from '@angular/material/select';
|
|
23
23
|
import { MatSelectModule } from '@angular/material/select';
|
|
24
24
|
import * as i12 from '@angular/material/list';
|
|
25
25
|
import { MatListModule } from '@angular/material/list';
|
|
@@ -27,7 +27,7 @@ import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
|
|
27
27
|
import { MatMenuModule } from '@angular/material/menu';
|
|
28
28
|
import * as i3$1 from '@angular/material/snack-bar';
|
|
29
29
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
|
30
|
-
import * as i9
|
|
30
|
+
import * as i9 from '@angular/material/tabs';
|
|
31
31
|
import { MatTabsModule } from '@angular/material/tabs';
|
|
32
32
|
import * as i8 from '@angular/cdk/scrolling';
|
|
33
33
|
import { ScrollingModule } from '@angular/cdk/scrolling';
|
|
@@ -514,16 +514,121 @@ class ConnectionBuilderComponent {
|
|
|
514
514
|
toggleShowOnlyIssues() { this.showOnlyIssues = !this.showOnlyIssues; }
|
|
515
515
|
toggleFriendly() { this.showFriendly = !this.showFriendly; }
|
|
516
516
|
trackByIndex = (i) => i;
|
|
517
|
+
isWidgetSource(from) {
|
|
518
|
+
return 'widget' in from;
|
|
519
|
+
}
|
|
520
|
+
isWidgetTarget(to) {
|
|
521
|
+
return 'widget' in to;
|
|
522
|
+
}
|
|
517
523
|
// Labels
|
|
518
|
-
fromLabel(c) {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
+
fromLabel(c) {
|
|
525
|
+
return this.isWidgetSource(c.from) ? `${c.from.widget}.${c.from.output}` : `state:${c.from.state}`;
|
|
526
|
+
}
|
|
527
|
+
toLabel(c) {
|
|
528
|
+
return this.isWidgetTarget(c.to) ? `${c.to.widget}.${c.to.input}` : `state:${c.to.state}`;
|
|
529
|
+
}
|
|
530
|
+
fromFriendly(c) {
|
|
531
|
+
return this.isWidgetSource(c.from) ? `${this.widgetFriendlyNameForKey(c.from.widget)}.${c.from.output}` : `state:${c.from.state}`;
|
|
532
|
+
}
|
|
533
|
+
toFriendly(c) {
|
|
534
|
+
return this.isWidgetTarget(c.to) ? `${this.widgetFriendlyNameForKey(c.to.widget)}.${c.to.input}` : `state:${c.to.state}`;
|
|
535
|
+
}
|
|
536
|
+
outputDescription(c) {
|
|
537
|
+
if (!this.isWidgetSource(c.from))
|
|
538
|
+
return '';
|
|
539
|
+
const from = c.from;
|
|
540
|
+
return this.registry.get(this.widgetTypeByKey(from.widget))?.outputs?.find(o => o.name === from.output)?.description || '';
|
|
541
|
+
}
|
|
542
|
+
inputDescription(c) {
|
|
543
|
+
if (!this.isWidgetTarget(c.to))
|
|
544
|
+
return '';
|
|
545
|
+
const to = c.to;
|
|
546
|
+
return this.registry.get(this.widgetTypeByKey(to.widget))?.inputs?.find(i => i.name === to.input)?.description || '';
|
|
547
|
+
}
|
|
548
|
+
fromTooltip(c) { return this.showFriendly ? `${this.fromFriendly(c)} → ${this.toFriendly(c)}` : ''; }
|
|
549
|
+
fromIcon(c) { return this.isWidgetSource(c.from) ? this.componentIconForKey(c.from.widget) : 'account_tree'; }
|
|
550
|
+
toIcon(c) { return this.isWidgetTarget(c.to) ? this.componentIconForKey(c.to.widget) : 'account_tree'; }
|
|
551
|
+
fromIconTooltip(c) { return this.isWidgetSource(c.from) ? this.widgetFriendlyNameForKey(c.from.widget) : this.fromLabel(c); }
|
|
552
|
+
toIconTooltip(c) { return this.isWidgetTarget(c.to) ? this.widgetFriendlyNameForKey(c.to.widget) : this.toLabel(c); }
|
|
524
553
|
widgetFriendlyNameForKey(key) { const id = this.widgetTypeByKey(key); return this.registry.get(id)?.friendlyName || id; }
|
|
525
554
|
componentIconForKey(key) { const id = this.widgetTypeByKey(key); return this.registry.get(id)?.icon || 'widgets'; }
|
|
526
555
|
widgetTypeByKey(key) { return this.widgets?.find(w => w.key === key)?.definition?.id || key; }
|
|
556
|
+
selectedConnection() {
|
|
557
|
+
const index = this.selectedIndex();
|
|
558
|
+
return index >= 0 ? this.connections()[index] : undefined;
|
|
559
|
+
}
|
|
560
|
+
selectedSourcePrimaryValue() {
|
|
561
|
+
const selected = this.selectedConnection();
|
|
562
|
+
if (!selected)
|
|
563
|
+
return '';
|
|
564
|
+
return this.isWidgetSource(selected.from) ? selected.from.widget : selected.from.state;
|
|
565
|
+
}
|
|
566
|
+
setSelectedSourcePrimaryValue(value) {
|
|
567
|
+
const selected = this.selectedConnection();
|
|
568
|
+
if (!selected)
|
|
569
|
+
return;
|
|
570
|
+
if (this.isWidgetSource(selected.from))
|
|
571
|
+
selected.from.widget = value;
|
|
572
|
+
else
|
|
573
|
+
selected.from.state = value;
|
|
574
|
+
this.connections.set([...this.connections()]);
|
|
575
|
+
}
|
|
576
|
+
selectedSourceSecondaryValue() {
|
|
577
|
+
const selected = this.selectedConnection();
|
|
578
|
+
return selected && this.isWidgetSource(selected.from) ? selected.from.output : '';
|
|
579
|
+
}
|
|
580
|
+
setSelectedSourceSecondaryValue(value) {
|
|
581
|
+
const selected = this.selectedConnection();
|
|
582
|
+
if (!selected || !this.isWidgetSource(selected.from))
|
|
583
|
+
return;
|
|
584
|
+
selected.from.output = value;
|
|
585
|
+
this.connections.set([...this.connections()]);
|
|
586
|
+
}
|
|
587
|
+
selectedTargetPrimaryValue() {
|
|
588
|
+
const selected = this.selectedConnection();
|
|
589
|
+
if (!selected)
|
|
590
|
+
return '';
|
|
591
|
+
return this.isWidgetTarget(selected.to) ? selected.to.widget : selected.to.state;
|
|
592
|
+
}
|
|
593
|
+
setSelectedTargetPrimaryValue(value) {
|
|
594
|
+
const selected = this.selectedConnection();
|
|
595
|
+
if (!selected)
|
|
596
|
+
return;
|
|
597
|
+
if (this.isWidgetTarget(selected.to))
|
|
598
|
+
selected.to.widget = value;
|
|
599
|
+
else
|
|
600
|
+
selected.to.state = value;
|
|
601
|
+
this.connections.set([...this.connections()]);
|
|
602
|
+
}
|
|
603
|
+
selectedTargetSecondaryValue() {
|
|
604
|
+
const selected = this.selectedConnection();
|
|
605
|
+
return selected && this.isWidgetTarget(selected.to) ? selected.to.input : '';
|
|
606
|
+
}
|
|
607
|
+
setSelectedTargetSecondaryValue(value) {
|
|
608
|
+
const selected = this.selectedConnection();
|
|
609
|
+
if (!selected || !this.isWidgetTarget(selected.to))
|
|
610
|
+
return;
|
|
611
|
+
selected.to.input = value;
|
|
612
|
+
this.connections.set([...this.connections()]);
|
|
613
|
+
}
|
|
614
|
+
selectedMapValue() {
|
|
615
|
+
return this.selectedConnection()?.map || '';
|
|
616
|
+
}
|
|
617
|
+
setSelectedMapValue(value) {
|
|
618
|
+
const selected = this.selectedConnection();
|
|
619
|
+
if (!selected)
|
|
620
|
+
return;
|
|
621
|
+
selected.map = value;
|
|
622
|
+
this.connections.set([...this.connections()]);
|
|
623
|
+
}
|
|
624
|
+
showSelectedSourceSecondary() {
|
|
625
|
+
const selected = this.selectedConnection();
|
|
626
|
+
return !!selected && this.isWidgetSource(selected.from);
|
|
627
|
+
}
|
|
628
|
+
showSelectedTargetSecondary() {
|
|
629
|
+
const selected = this.selectedConnection();
|
|
630
|
+
return !!selected && this.isWidgetTarget(selected.to);
|
|
631
|
+
}
|
|
527
632
|
applyFilters(list) {
|
|
528
633
|
const q = (this.filterText || '').toLowerCase();
|
|
529
634
|
const arr = list.filter((c) => {
|
|
@@ -531,16 +636,16 @@ class ConnectionBuilderComponent {
|
|
|
531
636
|
return false;
|
|
532
637
|
if (!q)
|
|
533
638
|
return true;
|
|
534
|
-
const hay = `${
|
|
639
|
+
const hay = `${this.fromLabel(c)} ${this.toLabel(c)} ${c.map || ''}`.toLowerCase();
|
|
535
640
|
return hay.includes(q);
|
|
536
641
|
});
|
|
537
642
|
return this.sortList(arr);
|
|
538
643
|
}
|
|
539
644
|
applyGrouping(list) {
|
|
540
645
|
switch (this.groupBy) {
|
|
541
|
-
case 'from': return this.groupByKey(list, c =>
|
|
542
|
-
case 'to': return this.groupByKey(list, c =>
|
|
543
|
-
case 'event': return this.groupByKey(list, c =>
|
|
646
|
+
case 'from': return this.groupByKey(list, c => this.fromLabel(c));
|
|
647
|
+
case 'to': return this.groupByKey(list, c => this.toLabel(c));
|
|
648
|
+
case 'event': return this.groupByKey(list, c => this.isWidgetSource(c.from) ? c.from.output : c.from.state);
|
|
544
649
|
default: return [{ label: 'Todas', list }];
|
|
545
650
|
}
|
|
546
651
|
}
|
|
@@ -577,14 +682,30 @@ class ConnectionBuilderComponent {
|
|
|
577
682
|
return; next.splice(index, 1); this.connections.set(next); }
|
|
578
683
|
// Validation
|
|
579
684
|
connectionStatus(c) {
|
|
580
|
-
if (
|
|
685
|
+
if (this.isWidgetSource(c.from)) {
|
|
686
|
+
const from = c.from;
|
|
687
|
+
if (!from.widget || !from.output)
|
|
688
|
+
return 'err';
|
|
689
|
+
const wFrom = this.widgetTypeByKey(from.widget);
|
|
690
|
+
const outOk = !!this.registry.get(wFrom)?.outputs?.some(o => o.name === from.output);
|
|
691
|
+
if (!outOk)
|
|
692
|
+
return 'warn';
|
|
693
|
+
}
|
|
694
|
+
else if (!c.from.state) {
|
|
581
695
|
return 'err';
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
696
|
+
}
|
|
697
|
+
if (this.isWidgetTarget(c.to)) {
|
|
698
|
+
const to = c.to;
|
|
699
|
+
if (!to.widget || !to.input)
|
|
700
|
+
return 'err';
|
|
701
|
+
const wTo = this.widgetTypeByKey(to.widget);
|
|
702
|
+
const inOk = !!this.registry.get(wTo)?.inputs?.some(i => i.name === to.input);
|
|
703
|
+
if (!inOk)
|
|
704
|
+
return 'warn';
|
|
705
|
+
}
|
|
706
|
+
else if (!c.to.state) {
|
|
707
|
+
return 'err';
|
|
708
|
+
}
|
|
588
709
|
return 'ok';
|
|
589
710
|
}
|
|
590
711
|
// Persist/apply
|
|
@@ -621,11 +742,11 @@ class ConnectionBuilderComponent {
|
|
|
621
742
|
return input;
|
|
622
743
|
}
|
|
623
744
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ConnectionBuilderComponent, deps: [{ token: i1.MatDialog }, { token: i1$1.ComponentMetadataRegistry }, { token: i3$1.MatSnackBar }], target: i0.ɵɵFactoryTarget.Component });
|
|
624
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: ConnectionBuilderComponent, isStandalone: true, selector: "praxis-connection-builder", inputs: { page: "page", widgets: "widgets" }, outputs: { pageChange: "pageChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"pdx-conn-root\" role=\"region\" aria-label=\"Construtor de Conex\u00F5es\">\n <div class=\"pdx-conn-head\">\n <span class=\"pdx-conn-title\">Conex\u00F5es</span>\n <span class=\"pdx-conn-count\" aria-label=\"Conex\u00F5es filtradas e total\">{{ filteredConnections().length }} / {{ connections().length }}</span>\n <mat-form-field appearance=\"outline\" class=\"pdx-conn-search\">\n <mat-label>Buscar</mat-label>\n <input matInput [(ngModel)]=\"filterText\" placeholder=\"Origem, Destino, Input, Map...\" />\n <button mat-icon-button matSuffix *ngIf=\"filterText\" (click)=\"filterText='';\" aria-label=\"Limpar busca\"><mat-icon [praxisIcon]=\"'close'\"></mat-icon></button>\n </mat-form-field>\n <span class=\"pdx-spacer\"></span>\n <button mat-button (click)=\"toggleShowOnlyIssues()\" [color]=\"showOnlyIssues ? 'primary': undefined\" aria-label=\"Somente avisos/erros\"><mat-icon [praxisIcon]=\"'report_problem'\"></mat-icon> Alertas</button>\n <mat-form-field appearance=\"outline\" class=\"pdx-conn-select\">\n <mat-label>Ordenar por</mat-label>\n <mat-select [(ngModel)]=\"sortBy\" (ngModelChange)=\"setSortBy($event)\">\n <mat-option value=\"from\">Origem</mat-option>\n <mat-option value=\"to\">Destino</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"pdx-conn-group\">\n <mat-label>Agrupar por</mat-label>\n <mat-select [(ngModel)]=\"groupBy\" (ngModelChange)=\"onGroupByChange()\">\n <mat-option value=\"none\">Nenhum</mat-option>\n <mat-option value=\"from\">Origem</mat-option>\n <mat-option value=\"to\">Destino</mat-option>\n <mat-option value=\"event\">Evento</mat-option>\n </mat-select>\n </mat-form-field>\n <button mat-icon-button [color]=\"showFriendly ? 'primary' : undefined\" (click)=\"toggleFriendly()\" aria-label=\"Alternar nome amig\u00E1vel/t\u00E9cnico\"><mat-icon [praxisIcon]=\"'translate'\"></mat-icon></button>\n <button mat-stroked-button (click)=\"createNew()\" aria-label=\"Nova conex\u00E3o\"><mat-icon [praxisIcon]=\"'add'\"></mat-icon> Nova conex\u00E3o</button>\n <button mat-flat-button color=\"accent\" class=\"pdx-diagram-cta\" (click)=\"openDiagramFullscreen()\" aria-label=\"Abrir diagrama em tela cheia\" matTooltip=\"Visualizar conex\u00F5es em grafo\"><mat-icon [praxisIcon]=\"'schema'\"></mat-icon><span>Diagrama</span></button>\n </div>\n\n <div class=\"pdx-conn-grid\">\n <!-- Left: read-only list -->\n <div class=\"pdx-conn-list\" role=\"list\" aria-label=\"Lista de conex\u00F5es\" cdkScrollable>\n <mat-list *ngIf=\"filteredConnections().length; else noConns\">\n <ng-container *ngIf=\"groupBy!=='none'; else flatList\">\n <div class=\"group-block\" *ngFor=\"let g of groupedConnections(); let gi = index\">\n <div class=\"group-header\">\n <span class=\"group-title\">{{ g.label }}</span>\n <span class=\"group-count\">{{ g.list.length }}</span>\n </div>\n <mat-divider></mat-divider>\n <div class=\"group-list\">\n <ng-container *ngFor=\"let c of g.list; let i = index\">\n <mat-list-item role=\"listitem\" (click)=\"startEdit(i, c)\" [class.selected]=\"selectedIndex()===i\">\n <div class=\"card-head\" matListItemTitle [matTooltip]=\"showFriendly ? (c.from.widget + '.' + c.from.output + ' \u2192 ' + (c.to.widget + '.' + c.to.input)) : ''\">\n <mat-icon class=\"comp-icon from\" [matTooltip]=\"widgetFriendlyNameForKey(c.from.widget)\">{{ componentIconForKey(c.from.widget) }}</mat-icon>\n <span class=\"pdx-badge from\" aria-hidden=\"true\"></span>\n <span class=\"from\">{{ showFriendly ? fromFriendly(c) : fromLabel(c) }}</span>\n <span class=\"arrow\">\u2192</span>\n <mat-icon class=\"comp-icon to\" [matTooltip]=\"widgetFriendlyNameForKey(c.to.widget)\">{{ componentIconForKey(c.to.widget) }}</mat-icon>\n <span class=\"pdx-badge to\" aria-hidden=\"true\"></span>\n <span class=\"to\">{{ showFriendly ? toFriendly(c) : toLabel(c) }}</span>\n <span class=\"spacer\"></span>\n <span class=\"status-pill\" [class.ok]=\"connectionStatus(c)==='ok'\" [class.warn]=\"connectionStatus(c)==='warn'\" [class.err]=\"connectionStatus(c)==='err'\">\n <mat-icon *ngIf=\"connectionStatus(c)==='ok'\">check_circle</mat-icon>\n <mat-icon *ngIf=\"connectionStatus(c)==='warn'\">warning</mat-icon>\n <mat-icon *ngIf=\"connectionStatus(c)==='err'\">error</mat-icon>\n </span>\n <button mat-icon-button class=\"expand-btn\" [attr.aria-expanded]=\"isExpanded(i)\" (click)=\"toggleExpanded(i, $event)\" [matTooltip]=\"isExpanded(i) ? 'Recolher detalhes' : 'Expandir detalhes'\">\n <mat-icon>{{ isExpanded(i) ? 'expand_less' : 'expand_more' }}</mat-icon>\n </button>\n </div>\n <div class=\"meta\" *ngIf=\"showFriendly\" matListItemLine>\n <span class=\"hint\">{{ outputDescription(c) }}</span>\n <span class=\"sep\" *ngIf=\"outputDescription(c) && inputDescription(c)\">\u2022</span>\n <span class=\"hint\">{{ inputDescription(c) }}</span>\n </div>\n <div class=\"map-chip\" *ngIf=\"c.map\" [matTooltip]=\"c.map || ''\" matListItemLine (click)=\"startEditByConn(c)\" title=\"Clique para editar Map\">\n <span class=\"pdx-badge map\" aria-hidden=\"true\"></span>\n <mat-icon class=\"map-icon\" inline>bolt</mat-icon>\n <span class=\"mono\">{{ c.map }}</span>\n </div>\n <div class=\"card-actions\" matListItemLine (click)=\"$event.stopPropagation()\">\n <span class=\"action-group\">\n <button mat-icon-button (click)=\"startEdit(i, c)\" matTooltip=\"Editar\"><mat-icon>settings</mat-icon></button>\n <button mat-icon-button (click)=\"duplicateConnection(i)\" matTooltip=\"Duplicar\"><mat-icon>content_copy</mat-icon></button>\n </span>\n <span class=\"action-group\">\n <button mat-stroked-button color=\"primary\" (click)=\"openDiagramFor(c)\" matTooltip=\"Ver rela\u00E7\u00E3o no diagrama\"><mat-icon>schema</mat-icon><span>Diagrama</span></button>\n </span>\n <span class=\"spacer\"></span>\n <span class=\"action-group\">\n <button mat-icon-button color=\"warn\" (click)=\"removeConnection(i)\" matTooltip=\"Remover\"><mat-icon>delete</mat-icon></button>\n </span>\n </div>\n <div class=\"card-details\" matListItemLine [class.expanded]=\"isExpanded(i)\" [style.maxHeight]=\"isExpanded(i) ? '240px' : '0px'\" [style.opacity]=\"isExpanded(i) ? 1 : 0\" [style.pointerEvents]=\"isExpanded(i) ? 'auto' : 'none'\">\n <div class=\"stepper\">\n <div class=\"step from-step\">\n <div class=\"dot from\"></div>\n <div class=\"content\">\n <div class=\"label\">De</div>\n <div class=\"value\">{{ showFriendly ? fromFriendly(c) : fromLabel(c) }}</div>\n </div>\n </div>\n <div class=\"step to-step\">\n <div class=\"dot to\"></div>\n <div class=\"content\">\n <div class=\"label\">Para</div>\n <div class=\"value\">{{ showFriendly ? toFriendly(c) : toLabel(c) }}</div>\n </div>\n </div>\n <div class=\"step map-step\" *ngIf=\"c.map\">\n <div class=\"dot map\"></div>\n <div class=\"content\">\n <div class=\"label\">Map</div>\n <div class=\"value mono\">{{ c.map }}</div>\n </div>\n </div>\n </div>\n </div>\n </mat-list-item>\n </ng-container>\n </div>\n </div>\n </ng-container>\n <ng-template #flatList>\n <ng-container *ngFor=\"let c of filteredConnections(); let i = index\">\n <mat-list-item role=\"listitem\" (click)=\"startEdit(i, c)\" [class.selected]=\"selectedIndex()===i\">\n <div class=\"card-head\" matListItemTitle [matTooltip]=\"showFriendly ? (c.from.widget + '.' + c.from.output + ' \u2192 ' + (c.to.widget + '.' + c.to.input)) : ''\">\n <mat-icon class=\"comp-icon from\" [matTooltip]=\"widgetFriendlyNameForKey(c.from.widget)\">{{ componentIconForKey(c.from.widget) }}</mat-icon>\n <span class=\"pdx-badge from\" aria-hidden=\"true\"></span>\n <span class=\"from\">{{ showFriendly ? fromFriendly(c) : fromLabel(c) }}</span>\n <span class=\"arrow\">\u2192</span>\n <mat-icon class=\"comp-icon to\" [matTooltip]=\"widgetFriendlyNameForKey(c.to.widget)\">{{ componentIconForKey(c.to.widget) }}</mat-icon>\n <span class=\"pdx-badge to\" aria-hidden=\"true\"></span>\n <span class=\"to\">{{ showFriendly ? toFriendly(c) : toLabel(c) }}</span>\n <span class=\"spacer\"></span>\n <span class=\"status-pill\" [class.ok]=\"connectionStatus(c)==='ok'\" [class.warn]=\"connectionStatus(c)==='warn'\" [class.err]=\"connectionStatus(c)==='err'\">\n <mat-icon *ngIf=\"connectionStatus(c)==='ok'\">check_circle</mat-icon>\n <mat-icon *ngIf=\"connectionStatus(c)==='warn'\">warning</mat-icon>\n <mat-icon *ngIf=\"connectionStatus(c)==='err'\">error</mat-icon>\n </span>\n <button mat-icon-button class=\"expand-btn\" [attr.aria-expanded]=\"isExpanded(i)\" (click)=\"toggleExpanded(i, $event)\" [matTooltip]=\"isExpanded(i) ? 'Recolher detalhes' : 'Expandir detalhes'\">\n <mat-icon>{{ isExpanded(i) ? 'expand_less' : 'expand_more' }}</mat-icon>\n </button>\n </div>\n <div class=\"meta\" *ngIf=\"showFriendly\" matListItemLine>\n <span class=\"hint\">{{ outputDescription(c) }}</span>\n <span class=\"sep\" *ngIf=\"outputDescription(c) && inputDescription(c)\">\u2022</span>\n <span class=\"hint\">{{ inputDescription(c) }}</span>\n </div>\n <div class=\"map-chip\" *ngIf=\"c.map\" [matTooltip]=\"c.map || ''\" matListItemLine (click)=\"startEditByConn(c)\" title=\"Clique para editar Map\">\n <span class=\"pdx-badge map\" aria-hidden=\"true\"></span>\n <mat-icon class=\"map-icon\" inline>bolt</mat-icon>\n <span class=\"mono\">{{ c.map }}</span>\n </div>\n <div class=\"card-actions\" matListItemLine (click)=\"$event.stopPropagation()\">\n <span class=\"action-group\">\n <button mat-icon-button (click)=\"startEdit(i, c)\" matTooltip=\"Editar\"><mat-icon>settings</mat-icon></button>\n <button mat-icon-button (click)=\"duplicateConnection(i)\" matTooltip=\"Duplicar\"><mat-icon>content_copy</mat-icon></button>\n </span>\n <span class=\"action-group\">\n <button mat-stroked-button color=\"primary\" (click)=\"openDiagramFor(c)\" matTooltip=\"Ver rela\u00E7\u00E3o no diagrama\"><mat-icon>schema</mat-icon><span>Diagrama</span></button>\n </span>\n <span class=\"spacer\"></span>\n <span class=\"action-group\">\n <button mat-icon-button color=\"warn\" (click)=\"removeConnection(i)\" matTooltip=\"Remover\"><mat-icon>delete</mat-icon></button>\n </span>\n </div>\n <div class=\"card-details\" matListItemLine [class.expanded]=\"isExpanded(i)\" [style.maxHeight]=\"isExpanded(i) ? '240px' : '0px'\" [style.opacity]=\"isExpanded(i) ? 1 : 0\" [style.pointerEvents]=\"isExpanded(i) ? 'auto' : 'none'\">\n <div class=\"stepper\">\n <div class=\"step from-step\">\n <div class=\"dot from\"></div>\n <div class=\"content\">\n <div class=\"label\">De</div>\n <div class=\"value\">{{ showFriendly ? fromFriendly(c) : fromLabel(c) }}</div>\n </div>\n </div>\n <div class=\"step to-step\">\n <div class=\"dot to\"></div>\n <div class=\"content\">\n <div class=\"label\">Para</div>\n <div class=\"value\">{{ showFriendly ? toFriendly(c) : toLabel(c) }}</div>\n </div>\n </div>\n <div class=\"step map-step\" *ngIf=\"c.map\">\n <div class=\"dot map\"></div>\n <div class=\"content\">\n <div class=\"label\">Map</div>\n <div class=\"value mono\">{{ c.map }}</div>\n </div>\n </div>\n </div>\n </div>\n </mat-list-item>\n </ng-container>\n </ng-template>\n </mat-list>\n <ng-template #noConns>\n <div class=\"pdx-empty-list\" role=\"status\" aria-live=\"polite\">\n <mat-icon>link_off</mat-icon>\n <div>Nenhuma conex\u00E3o definida. Use \"Nova conex\u00E3o\".</div>\n </div>\n </ng-template>\n </div>\n\n <!-- Right: editor form -->\n <div class=\"pdx-conn-editor\" role=\"form\" aria-label=\"Editor de conex\u00E3o\">\n <ng-container *ngIf=\"selectedIndex() >= 0; else emptyEditor\">\n <div class=\"form-grid\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Origem (widget.key)</mat-label>\n <input matInput [(ngModel)]=\"connections()[selectedIndex()].from.widget\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Evento (output)</mat-label>\n <input matInput [(ngModel)]=\"connections()[selectedIndex()].from.output\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Destino (widget.key)</mat-label>\n <input matInput [(ngModel)]=\"connections()[selectedIndex()].to.widget\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Input</mat-label>\n <input matInput [(ngModel)]=\"connections()[selectedIndex()].to.input\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"full-span\">\n <mat-label>Map (opcional)</mat-label>\n <input matInput [(ngModel)]=\"connections()[selectedIndex()].map\" [placeholder]=\"mapPlaceholder\" />\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n class=\"help-icon-button\"\n matTooltip=\"Ex.: payload.id ou ${payload.id}.\"\n >\n <mat-icon>help_outline</mat-icon>\n </button>\n </mat-form-field>\n <div class=\"full-span\">\n <button mat-flat-button color=\"primary\" (click)=\"onSave()\"><mat-icon [praxisIcon]=\"'save'\"></mat-icon>Salvar</button>\n </div>\n </div>\n </ng-container>\n <ng-template #emptyEditor>\n <div class=\"pdx-empty-editor\">Selecione uma conex\u00E3o para editar.</div>\n </ng-template>\n </div>\n </div>\n</div>\n", styles: [".pdx-conn-root{display:grid;gap:12px;color:var(--md-sys-color-on-surface)}.pdx-conn-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.pdx-conn-title{font-weight:600}.pdx-conn-count{color:var(--md-sys-color-on-surface-variant)}.pdx-conn-search{width:260px}.pdx-conn-select{width:160px}.pdx-conn-group{width:170px}.pdx-spacer{flex:1}.pdx-diagram-cta span{margin-left:6px}.pdx-conn-grid{display:grid;grid-template-columns:1.4fr 1fr;gap:12px}.pdx-conn-list{height:540px;overflow:auto;padding:8px 0;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;background:var(--md-sys-color-surface)}.pdx-conn-list .mat-mdc-list-item.mdc-list-item{height:auto;padding:10px 8px 8px}.pdx-conn-list .mat-mdc-list-item.selected{background:var(--md-sys-color-primary-container)}.card-head{display:flex;align-items:center;gap:6px}.card-head .spacer{flex:1}.card-head .comp-icon{opacity:.85}.card-head .expand-btn{margin-left:4px}.status-pill{display:inline-flex;align-items:center;gap:4px;border-radius:999px;padding:2px 8px}.status-pill.ok{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.status-pill.warn{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container)}.status-pill.err{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.map-chip{display:inline-flex;align-items:center;gap:6px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.map-chip .map-icon{font-size:16px}.pdx-conn-editor{min-height:540px;padding:8px;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;background:var(--md-sys-color-surface)}.pdx-empty-list{padding:16px;display:grid;place-items:center;gap:6px;opacity:.8}.pdx-empty-editor{padding:16px;opacity:.75}.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;align-content:start}.full-span{grid-column:1/-1}.stepper{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;padding:8px 0}.step{display:grid;grid-template-columns:12px 1fr;gap:8px;align-items:start}.dot{width:12px;height:12px;border-radius:50%;margin-top:5px}.dot.from{background:var(--md-sys-color-primary-container)}.dot.to{background:var(--md-sys-color-secondary-container)}.dot.map{background:var(--md-sys-color-tertiary-container)}.group-block{padding:6px 6px 10px}.group-header{display:flex;gap:8px;align-items:center;padding:8px 0}.group-title{font-weight:600}.group-count{opacity:.75}.group-list{display:grid;gap:6px}.pdx-badge{width:10px;height:10px;border-radius:50%;display:inline-block}.pdx-badge.from{background:var(--md-sys-color-primary-container)}.pdx-badge.to{background:var(--md-sys-color-secondary-container)}.pdx-badge.map{background:var(--md-sys-color-tertiary-container)}.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px}.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;vertical-align:middle}.help-icon-button mat-icon{font-size:18px;line-height:18px;width:18px;height:18px}@media(max-width:960px){.pdx-conn-grid{grid-template-columns:1fr}.pdx-conn-list,.pdx-conn-editor{min-height:360px}.pdx-conn-search,.pdx-conn-select,.pdx-conn-group{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3$2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.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: "ngmodule", type: MatSelectModule }, { kind: "directive", type: i8.CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }, { kind: "component", type: i9.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: i9.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i12.MatList, selector: "mat-list", exportAs: ["matList"] }, { kind: "component", type: i12.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i12.MatListItemLine, selector: "[matListItemLine]" }, { kind: "directive", type: i12.MatListItemTitle, selector: "[matListItemTitle]" }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "ngmodule", type: MatMenuModule }, { kind: "ngmodule", type: MatTabsModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
745
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: ConnectionBuilderComponent, isStandalone: true, selector: "praxis-connection-builder", inputs: { page: "page", widgets: "widgets" }, outputs: { pageChange: "pageChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"pdx-conn-root\" role=\"region\" aria-label=\"Construtor de Conex\u00C3\u00B5es\">\n <div class=\"pdx-conn-head\">\n <span class=\"pdx-conn-title\">Conex\u00C3\u00B5es</span>\n <span class=\"pdx-conn-count\" aria-label=\"Conex\u00C3\u00B5es filtradas e total\">{{ filteredConnections().length }} / {{ connections().length }}</span>\n <mat-form-field appearance=\"outline\" class=\"pdx-conn-search\">\n <mat-label>Buscar</mat-label>\n <input matInput [(ngModel)]=\"filterText\" placeholder=\"Origem, Destino, Input, Map...\" />\n <button mat-icon-button matSuffix *ngIf=\"filterText\" (click)=\"filterText='';\" aria-label=\"Limpar busca\"><mat-icon [praxisIcon]=\"'close'\"></mat-icon></button>\n </mat-form-field>\n <span class=\"pdx-spacer\"></span>\n <button mat-button (click)=\"toggleShowOnlyIssues()\" [color]=\"showOnlyIssues ? 'primary': undefined\" aria-label=\"Somente avisos/erros\"><mat-icon [praxisIcon]=\"'report_problem'\"></mat-icon> Alertas</button>\n <mat-form-field appearance=\"outline\" class=\"pdx-conn-select\">\n <mat-label>Ordenar por</mat-label>\n <mat-select [(ngModel)]=\"sortBy\" (ngModelChange)=\"setSortBy($event)\">\n <mat-option value=\"from\">Origem</mat-option>\n <mat-option value=\"to\">Destino</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"pdx-conn-group\">\n <mat-label>Agrupar por</mat-label>\n <mat-select [(ngModel)]=\"groupBy\" (ngModelChange)=\"onGroupByChange()\">\n <mat-option value=\"none\">Nenhum</mat-option>\n <mat-option value=\"from\">Origem</mat-option>\n <mat-option value=\"to\">Destino</mat-option>\n <mat-option value=\"event\">Evento</mat-option>\n </mat-select>\n </mat-form-field>\n <button mat-icon-button [color]=\"showFriendly ? 'primary' : undefined\" (click)=\"toggleFriendly()\" aria-label=\"Alternar nome amig\u00C3\u00A1vel/t\u00C3\u00A9cnico\"><mat-icon [praxisIcon]=\"'translate'\"></mat-icon></button>\n <button mat-stroked-button (click)=\"createNew()\" aria-label=\"Nova conex\u00C3\u00A3o\"><mat-icon [praxisIcon]=\"'add'\"></mat-icon> Nova conex\u00C3\u00A3o</button>\n <button mat-flat-button color=\"accent\" class=\"pdx-diagram-cta\" (click)=\"openDiagramFullscreen()\" aria-label=\"Abrir diagrama em tela cheia\" matTooltip=\"Visualizar conex\u00C3\u00B5es em grafo\"><mat-icon [praxisIcon]=\"'schema'\"></mat-icon><span>Diagrama</span></button>\n </div>\n\n <div class=\"pdx-conn-grid\">\n <div class=\"pdx-conn-list\" role=\"list\" aria-label=\"Lista de conex\u00C3\u00B5es\" cdkScrollable>\n <mat-list *ngIf=\"filteredConnections().length; else noConns\">\n <ng-container *ngIf=\"groupBy!=='none'; else flatList\">\n <div class=\"group-block\" *ngFor=\"let g of groupedConnections(); let gi = index\">\n <div class=\"group-header\">\n <span class=\"group-title\">{{ g.label }}</span>\n <span class=\"group-count\">{{ g.list.length }}</span>\n </div>\n <mat-divider></mat-divider>\n <div class=\"group-list\">\n <ng-container *ngFor=\"let c of g.list; let i = index\">\n <mat-list-item role=\"listitem\" (click)=\"startEdit(i, c)\" [class.selected]=\"selectedIndex()===i\">\n <div class=\"card-head\" matListItemTitle [matTooltip]=\"fromTooltip(c)\">\n <mat-icon class=\"comp-icon from\" [matTooltip]=\"fromIconTooltip(c)\">{{ fromIcon(c) }}</mat-icon>\n <span class=\"pdx-badge from\" aria-hidden=\"true\"></span>\n <span class=\"from\">{{ showFriendly ? fromFriendly(c) : fromLabel(c) }}</span>\n <span class=\"arrow\">\u00E2\u2020\u2019</span>\n <mat-icon class=\"comp-icon to\" [matTooltip]=\"toIconTooltip(c)\">{{ toIcon(c) }}</mat-icon>\n <span class=\"pdx-badge to\" aria-hidden=\"true\"></span>\n <span class=\"to\">{{ showFriendly ? toFriendly(c) : toLabel(c) }}</span>\n <span class=\"spacer\"></span>\n <span class=\"status-pill\" [class.ok]=\"connectionStatus(c)==='ok'\" [class.warn]=\"connectionStatus(c)==='warn'\" [class.err]=\"connectionStatus(c)==='err'\">\n <mat-icon *ngIf=\"connectionStatus(c)==='ok'\">check_circle</mat-icon>\n <mat-icon *ngIf=\"connectionStatus(c)==='warn'\">warning</mat-icon>\n <mat-icon *ngIf=\"connectionStatus(c)==='err'\">error</mat-icon>\n </span>\n <button mat-icon-button class=\"expand-btn\" [attr.aria-expanded]=\"isExpanded(i)\" (click)=\"toggleExpanded(i, $event)\" [matTooltip]=\"isExpanded(i) ? 'Recolher detalhes' : 'Expandir detalhes'\">\n <mat-icon>{{ isExpanded(i) ? 'expand_less' : 'expand_more' }}</mat-icon>\n </button>\n </div>\n <div class=\"meta\" *ngIf=\"showFriendly\" matListItemLine>\n <span class=\"hint\">{{ outputDescription(c) }}</span>\n <span class=\"sep\" *ngIf=\"outputDescription(c) && inputDescription(c)\">\u00E2\u20AC\u00A2</span>\n <span class=\"hint\">{{ inputDescription(c) }}</span>\n </div>\n <div class=\"map-chip\" *ngIf=\"c.map\" [matTooltip]=\"c.map || ''\" matListItemLine (click)=\"startEditByConn(c)\" title=\"Clique para editar Map\">\n <span class=\"pdx-badge map\" aria-hidden=\"true\"></span>\n <mat-icon class=\"map-icon\" inline>bolt</mat-icon>\n <span class=\"mono\">{{ c.map }}</span>\n </div>\n <div class=\"card-actions\" matListItemLine (click)=\"$event.stopPropagation()\">\n <span class=\"action-group\">\n <button mat-icon-button (click)=\"startEdit(i, c)\" matTooltip=\"Editar\"><mat-icon>settings</mat-icon></button>\n <button mat-icon-button (click)=\"duplicateConnection(i)\" matTooltip=\"Duplicar\"><mat-icon>content_copy</mat-icon></button>\n </span>\n <span class=\"action-group\">\n <button mat-stroked-button color=\"primary\" (click)=\"openDiagramFor(c)\" matTooltip=\"Ver rela\u00C3\u00A7\u00C3\u00A3o no diagrama\"><mat-icon>schema</mat-icon><span>Diagrama</span></button>\n </span>\n <span class=\"spacer\"></span>\n <span class=\"action-group\">\n <button mat-icon-button color=\"warn\" (click)=\"removeConnection(i)\" matTooltip=\"Remover\"><mat-icon>delete</mat-icon></button>\n </span>\n </div>\n <div class=\"card-details\" matListItemLine [class.expanded]=\"isExpanded(i)\" [style.maxHeight]=\"isExpanded(i) ? '240px' : '0px'\" [style.opacity]=\"isExpanded(i) ? 1 : 0\" [style.pointerEvents]=\"isExpanded(i) ? 'auto' : 'none'\">\n <div class=\"stepper\">\n <div class=\"step from-step\">\n <div class=\"dot from\"></div>\n <div class=\"content\">\n <div class=\"label\">De</div>\n <div class=\"value\">{{ showFriendly ? fromFriendly(c) : fromLabel(c) }}</div>\n </div>\n </div>\n <div class=\"step to-step\">\n <div class=\"dot to\"></div>\n <div class=\"content\">\n <div class=\"label\">Para</div>\n <div class=\"value\">{{ showFriendly ? toFriendly(c) : toLabel(c) }}</div>\n </div>\n </div>\n <div class=\"step map-step\" *ngIf=\"c.map\">\n <div class=\"dot map\"></div>\n <div class=\"content\">\n <div class=\"label\">Map</div>\n <div class=\"value mono\">{{ c.map }}</div>\n </div>\n </div>\n </div>\n </div>\n </mat-list-item>\n </ng-container>\n </div>\n </div>\n </ng-container>\n <ng-template #flatList>\n <ng-container *ngFor=\"let c of filteredConnections(); let i = index\">\n <mat-list-item role=\"listitem\" (click)=\"startEdit(i, c)\" [class.selected]=\"selectedIndex()===i\">\n <div class=\"card-head\" matListItemTitle [matTooltip]=\"fromTooltip(c)\">\n <mat-icon class=\"comp-icon from\" [matTooltip]=\"fromIconTooltip(c)\">{{ fromIcon(c) }}</mat-icon>\n <span class=\"pdx-badge from\" aria-hidden=\"true\"></span>\n <span class=\"from\">{{ showFriendly ? fromFriendly(c) : fromLabel(c) }}</span>\n <span class=\"arrow\">\u00E2\u2020\u2019</span>\n <mat-icon class=\"comp-icon to\" [matTooltip]=\"toIconTooltip(c)\">{{ toIcon(c) }}</mat-icon>\n <span class=\"pdx-badge to\" aria-hidden=\"true\"></span>\n <span class=\"to\">{{ showFriendly ? toFriendly(c) : toLabel(c) }}</span>\n <span class=\"spacer\"></span>\n <span class=\"status-pill\" [class.ok]=\"connectionStatus(c)==='ok'\" [class.warn]=\"connectionStatus(c)==='warn'\" [class.err]=\"connectionStatus(c)==='err'\">\n <mat-icon *ngIf=\"connectionStatus(c)==='ok'\">check_circle</mat-icon>\n <mat-icon *ngIf=\"connectionStatus(c)==='warn'\">warning</mat-icon>\n <mat-icon *ngIf=\"connectionStatus(c)==='err'\">error</mat-icon>\n </span>\n <button mat-icon-button class=\"expand-btn\" [attr.aria-expanded]=\"isExpanded(i)\" (click)=\"toggleExpanded(i, $event)\" [matTooltip]=\"isExpanded(i) ? 'Recolher detalhes' : 'Expandir detalhes'\">\n <mat-icon>{{ isExpanded(i) ? 'expand_less' : 'expand_more' }}</mat-icon>\n </button>\n </div>\n <div class=\"meta\" *ngIf=\"showFriendly\" matListItemLine>\n <span class=\"hint\">{{ outputDescription(c) }}</span>\n <span class=\"sep\" *ngIf=\"outputDescription(c) && inputDescription(c)\">\u00E2\u20AC\u00A2</span>\n <span class=\"hint\">{{ inputDescription(c) }}</span>\n </div>\n <div class=\"map-chip\" *ngIf=\"c.map\" [matTooltip]=\"c.map || ''\" matListItemLine (click)=\"startEditByConn(c)\" title=\"Clique para editar Map\">\n <span class=\"pdx-badge map\" aria-hidden=\"true\"></span>\n <mat-icon class=\"map-icon\" inline>bolt</mat-icon>\n <span class=\"mono\">{{ c.map }}</span>\n </div>\n <div class=\"card-actions\" matListItemLine (click)=\"$event.stopPropagation()\">\n <span class=\"action-group\">\n <button mat-icon-button (click)=\"startEdit(i, c)\" matTooltip=\"Editar\"><mat-icon>settings</mat-icon></button>\n <button mat-icon-button (click)=\"duplicateConnection(i)\" matTooltip=\"Duplicar\"><mat-icon>content_copy</mat-icon></button>\n </span>\n <span class=\"action-group\">\n <button mat-stroked-button color=\"primary\" (click)=\"openDiagramFor(c)\" matTooltip=\"Ver rela\u00C3\u00A7\u00C3\u00A3o no diagrama\"><mat-icon>schema</mat-icon><span>Diagrama</span></button>\n </span>\n <span class=\"spacer\"></span>\n <span class=\"action-group\">\n <button mat-icon-button color=\"warn\" (click)=\"removeConnection(i)\" matTooltip=\"Remover\"><mat-icon>delete</mat-icon></button>\n </span>\n </div>\n <div class=\"card-details\" matListItemLine [class.expanded]=\"isExpanded(i)\" [style.maxHeight]=\"isExpanded(i) ? '240px' : '0px'\" [style.opacity]=\"isExpanded(i) ? 1 : 0\" [style.pointerEvents]=\"isExpanded(i) ? 'auto' : 'none'\">\n <div class=\"stepper\">\n <div class=\"step from-step\">\n <div class=\"dot from\"></div>\n <div class=\"content\">\n <div class=\"label\">De</div>\n <div class=\"value\">{{ showFriendly ? fromFriendly(c) : fromLabel(c) }}</div>\n </div>\n </div>\n <div class=\"step to-step\">\n <div class=\"dot to\"></div>\n <div class=\"content\">\n <div class=\"label\">Para</div>\n <div class=\"value\">{{ showFriendly ? toFriendly(c) : toLabel(c) }}</div>\n </div>\n </div>\n <div class=\"step map-step\" *ngIf=\"c.map\">\n <div class=\"dot map\"></div>\n <div class=\"content\">\n <div class=\"label\">Map</div>\n <div class=\"value mono\">{{ c.map }}</div>\n </div>\n </div>\n </div>\n </div>\n </mat-list-item>\n </ng-container>\n </ng-template>\n </mat-list>\n <ng-template #noConns>\n <div class=\"pdx-empty-list\" role=\"status\" aria-live=\"polite\">\n <mat-icon>link_off</mat-icon>\n <div>Nenhuma conex\u00C3\u00A3o definida. Use \"Nova conex\u00C3\u00A3o\".</div>\n </div>\n </ng-template>\n </div>\n\n <div class=\"pdx-conn-editor\" role=\"form\" aria-label=\"Editor de conex\u00C3\u00A3o\">\n <ng-container *ngIf=\"selectedIndex() >= 0; else emptyEditor\">\n <div class=\"form-grid\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Origem (widget.key)</mat-label>\n <input matInput [ngModel]=\"selectedSourcePrimaryValue()\" (ngModelChange)=\"setSelectedSourcePrimaryValue($event)\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" *ngIf=\"showSelectedSourceSecondary()\">\n <mat-label>Evento (output)</mat-label>\n <input matInput [ngModel]=\"selectedSourceSecondaryValue()\" (ngModelChange)=\"setSelectedSourceSecondaryValue($event)\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Destino (widget.key)</mat-label>\n <input matInput [ngModel]=\"selectedTargetPrimaryValue()\" (ngModelChange)=\"setSelectedTargetPrimaryValue($event)\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" *ngIf=\"showSelectedTargetSecondary()\">\n <mat-label>Input</mat-label>\n <input matInput [ngModel]=\"selectedTargetSecondaryValue()\" (ngModelChange)=\"setSelectedTargetSecondaryValue($event)\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"full-span\">\n <mat-label>Map (opcional)</mat-label>\n <input matInput [ngModel]=\"selectedMapValue()\" (ngModelChange)=\"setSelectedMapValue($event)\" [placeholder]=\"mapPlaceholder\" />\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n class=\"help-icon-button\"\n matTooltip=\"Ex.: payload.id ou ${payload.id}.\"\n >\n <mat-icon>help_outline</mat-icon>\n </button>\n </mat-form-field>\n <div class=\"full-span\">\n <button mat-flat-button color=\"primary\" (click)=\"onSave()\"><mat-icon [praxisIcon]=\"'save'\"></mat-icon>Salvar</button>\n </div>\n </div>\n </ng-container>\n <ng-template #emptyEditor>\n <div class=\"pdx-empty-editor\">Selecione uma conex\u00C3\u00A3o para editar.</div>\n </ng-template>\n </div>\n </div>\n</div>\n", styles: [".pdx-conn-root{display:grid;gap:12px;color:var(--md-sys-color-on-surface)}.pdx-conn-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.pdx-conn-title{font-weight:600}.pdx-conn-count{color:var(--md-sys-color-on-surface-variant)}.pdx-conn-search{width:260px}.pdx-conn-select{width:160px}.pdx-conn-group{width:170px}.pdx-spacer{flex:1}.pdx-diagram-cta span{margin-left:6px}.pdx-conn-grid{display:grid;grid-template-columns:1.4fr 1fr;gap:12px}.pdx-conn-list{height:540px;overflow:auto;padding:8px 0;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;background:var(--md-sys-color-surface)}.pdx-conn-list .mat-mdc-list-item.mdc-list-item{height:auto;padding:10px 8px 8px}.pdx-conn-list .mat-mdc-list-item.selected{background:var(--md-sys-color-primary-container)}.card-head{display:flex;align-items:center;gap:6px}.card-head .spacer{flex:1}.card-head .comp-icon{opacity:.85}.card-head .expand-btn{margin-left:4px}.status-pill{display:inline-flex;align-items:center;gap:4px;border-radius:999px;padding:2px 8px}.status-pill.ok{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.status-pill.warn{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container)}.status-pill.err{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.map-chip{display:inline-flex;align-items:center;gap:6px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.map-chip .map-icon{font-size:16px}.pdx-conn-editor{min-height:540px;padding:8px;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;background:var(--md-sys-color-surface)}.pdx-empty-list{padding:16px;display:grid;place-items:center;gap:6px;opacity:.8}.pdx-empty-editor{padding:16px;opacity:.75}.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;align-content:start}.full-span{grid-column:1/-1}.stepper{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;padding:8px 0}.step{display:grid;grid-template-columns:12px 1fr;gap:8px;align-items:start}.dot{width:12px;height:12px;border-radius:50%;margin-top:5px}.dot.from{background:var(--md-sys-color-primary-container)}.dot.to{background:var(--md-sys-color-secondary-container)}.dot.map{background:var(--md-sys-color-tertiary-container)}.group-block{padding:6px 6px 10px}.group-header{display:flex;gap:8px;align-items:center;padding:8px 0}.group-title{font-weight:600}.group-count{opacity:.75}.group-list{display:grid;gap:6px}.pdx-badge{width:10px;height:10px;border-radius:50%;display:inline-block}.pdx-badge.from{background:var(--md-sys-color-primary-container)}.pdx-badge.to{background:var(--md-sys-color-secondary-container)}.pdx-badge.map{background:var(--md-sys-color-tertiary-container)}.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px}.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;vertical-align:middle}.help-icon-button mat-icon{font-size:18px;line-height:18px;width:18px;height:18px}@media(max-width:960px){.pdx-conn-grid{grid-template-columns:1fr}.pdx-conn-list,.pdx-conn-editor{min-height:360px}.pdx-conn-search,.pdx-conn-select,.pdx-conn-group{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3$2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.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: "ngmodule", type: MatSelectModule }, { kind: "directive", type: i8.CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }, { kind: "component", type: i6.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: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i12.MatList, selector: "mat-list", exportAs: ["matList"] }, { kind: "component", type: i12.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i12.MatListItemLine, selector: "[matListItemLine]" }, { kind: "directive", type: i12.MatListItemTitle, selector: "[matListItemTitle]" }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "ngmodule", type: MatMenuModule }, { kind: "ngmodule", type: MatTabsModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
625
746
|
}
|
|
626
747
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ConnectionBuilderComponent, decorators: [{
|
|
627
748
|
type: Component,
|
|
628
|
-
args: [{ selector: 'praxis-connection-builder', standalone: true, imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule, MatSelectModule, MatButtonModule, MatIconModule, MatListModule, MatDialogModule, MatAutocompleteModule, MatMenuModule, MatTabsModule, MatTooltipModule, MatProgressBarModule, MatCheckboxModule, MatSnackBarModule, ScrollingModule, PraxisIconDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"pdx-conn-root\" role=\"region\" aria-label=\"Construtor de Conex\u00F5es\">\n <div class=\"pdx-conn-head\">\n <span class=\"pdx-conn-title\">Conex\u00F5es</span>\n <span class=\"pdx-conn-count\" aria-label=\"Conex\u00F5es filtradas e total\">{{ filteredConnections().length }} / {{ connections().length }}</span>\n <mat-form-field appearance=\"outline\" class=\"pdx-conn-search\">\n <mat-label>Buscar</mat-label>\n <input matInput [(ngModel)]=\"filterText\" placeholder=\"Origem, Destino, Input, Map...\" />\n <button mat-icon-button matSuffix *ngIf=\"filterText\" (click)=\"filterText='';\" aria-label=\"Limpar busca\"><mat-icon [praxisIcon]=\"'close'\"></mat-icon></button>\n </mat-form-field>\n <span class=\"pdx-spacer\"></span>\n <button mat-button (click)=\"toggleShowOnlyIssues()\" [color]=\"showOnlyIssues ? 'primary': undefined\" aria-label=\"Somente avisos/erros\"><mat-icon [praxisIcon]=\"'report_problem'\"></mat-icon> Alertas</button>\n <mat-form-field appearance=\"outline\" class=\"pdx-conn-select\">\n <mat-label>Ordenar por</mat-label>\n <mat-select [(ngModel)]=\"sortBy\" (ngModelChange)=\"setSortBy($event)\">\n <mat-option value=\"from\">Origem</mat-option>\n <mat-option value=\"to\">Destino</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"pdx-conn-group\">\n <mat-label>Agrupar por</mat-label>\n <mat-select [(ngModel)]=\"groupBy\" (ngModelChange)=\"onGroupByChange()\">\n <mat-option value=\"none\">Nenhum</mat-option>\n <mat-option value=\"from\">Origem</mat-option>\n <mat-option value=\"to\">Destino</mat-option>\n <mat-option value=\"event\">Evento</mat-option>\n </mat-select>\n </mat-form-field>\n <button mat-icon-button [color]=\"showFriendly ? 'primary' : undefined\" (click)=\"toggleFriendly()\" aria-label=\"Alternar nome amig\u00E1vel/t\u00E9cnico\"><mat-icon [praxisIcon]=\"'translate'\"></mat-icon></button>\n <button mat-stroked-button (click)=\"createNew()\" aria-label=\"Nova conex\u00E3o\"><mat-icon [praxisIcon]=\"'add'\"></mat-icon> Nova conex\u00E3o</button>\n <button mat-flat-button color=\"accent\" class=\"pdx-diagram-cta\" (click)=\"openDiagramFullscreen()\" aria-label=\"Abrir diagrama em tela cheia\" matTooltip=\"Visualizar conex\u00F5es em grafo\"><mat-icon [praxisIcon]=\"'schema'\"></mat-icon><span>Diagrama</span></button>\n </div>\n\n <div class=\"pdx-conn-grid\">\n <!-- Left: read-only list -->\n <div class=\"pdx-conn-list\" role=\"list\" aria-label=\"Lista de conex\u00F5es\" cdkScrollable>\n <mat-list *ngIf=\"filteredConnections().length; else noConns\">\n <ng-container *ngIf=\"groupBy!=='none'; else flatList\">\n <div class=\"group-block\" *ngFor=\"let g of groupedConnections(); let gi = index\">\n <div class=\"group-header\">\n <span class=\"group-title\">{{ g.label }}</span>\n <span class=\"group-count\">{{ g.list.length }}</span>\n </div>\n <mat-divider></mat-divider>\n <div class=\"group-list\">\n <ng-container *ngFor=\"let c of g.list; let i = index\">\n <mat-list-item role=\"listitem\" (click)=\"startEdit(i, c)\" [class.selected]=\"selectedIndex()===i\">\n <div class=\"card-head\" matListItemTitle [matTooltip]=\"showFriendly ? (c.from.widget + '.' + c.from.output + ' \u2192 ' + (c.to.widget + '.' + c.to.input)) : ''\">\n <mat-icon class=\"comp-icon from\" [matTooltip]=\"widgetFriendlyNameForKey(c.from.widget)\">{{ componentIconForKey(c.from.widget) }}</mat-icon>\n <span class=\"pdx-badge from\" aria-hidden=\"true\"></span>\n <span class=\"from\">{{ showFriendly ? fromFriendly(c) : fromLabel(c) }}</span>\n <span class=\"arrow\">\u2192</span>\n <mat-icon class=\"comp-icon to\" [matTooltip]=\"widgetFriendlyNameForKey(c.to.widget)\">{{ componentIconForKey(c.to.widget) }}</mat-icon>\n <span class=\"pdx-badge to\" aria-hidden=\"true\"></span>\n <span class=\"to\">{{ showFriendly ? toFriendly(c) : toLabel(c) }}</span>\n <span class=\"spacer\"></span>\n <span class=\"status-pill\" [class.ok]=\"connectionStatus(c)==='ok'\" [class.warn]=\"connectionStatus(c)==='warn'\" [class.err]=\"connectionStatus(c)==='err'\">\n <mat-icon *ngIf=\"connectionStatus(c)==='ok'\">check_circle</mat-icon>\n <mat-icon *ngIf=\"connectionStatus(c)==='warn'\">warning</mat-icon>\n <mat-icon *ngIf=\"connectionStatus(c)==='err'\">error</mat-icon>\n </span>\n <button mat-icon-button class=\"expand-btn\" [attr.aria-expanded]=\"isExpanded(i)\" (click)=\"toggleExpanded(i, $event)\" [matTooltip]=\"isExpanded(i) ? 'Recolher detalhes' : 'Expandir detalhes'\">\n <mat-icon>{{ isExpanded(i) ? 'expand_less' : 'expand_more' }}</mat-icon>\n </button>\n </div>\n <div class=\"meta\" *ngIf=\"showFriendly\" matListItemLine>\n <span class=\"hint\">{{ outputDescription(c) }}</span>\n <span class=\"sep\" *ngIf=\"outputDescription(c) && inputDescription(c)\">\u2022</span>\n <span class=\"hint\">{{ inputDescription(c) }}</span>\n </div>\n <div class=\"map-chip\" *ngIf=\"c.map\" [matTooltip]=\"c.map || ''\" matListItemLine (click)=\"startEditByConn(c)\" title=\"Clique para editar Map\">\n <span class=\"pdx-badge map\" aria-hidden=\"true\"></span>\n <mat-icon class=\"map-icon\" inline>bolt</mat-icon>\n <span class=\"mono\">{{ c.map }}</span>\n </div>\n <div class=\"card-actions\" matListItemLine (click)=\"$event.stopPropagation()\">\n <span class=\"action-group\">\n <button mat-icon-button (click)=\"startEdit(i, c)\" matTooltip=\"Editar\"><mat-icon>settings</mat-icon></button>\n <button mat-icon-button (click)=\"duplicateConnection(i)\" matTooltip=\"Duplicar\"><mat-icon>content_copy</mat-icon></button>\n </span>\n <span class=\"action-group\">\n <button mat-stroked-button color=\"primary\" (click)=\"openDiagramFor(c)\" matTooltip=\"Ver rela\u00E7\u00E3o no diagrama\"><mat-icon>schema</mat-icon><span>Diagrama</span></button>\n </span>\n <span class=\"spacer\"></span>\n <span class=\"action-group\">\n <button mat-icon-button color=\"warn\" (click)=\"removeConnection(i)\" matTooltip=\"Remover\"><mat-icon>delete</mat-icon></button>\n </span>\n </div>\n <div class=\"card-details\" matListItemLine [class.expanded]=\"isExpanded(i)\" [style.maxHeight]=\"isExpanded(i) ? '240px' : '0px'\" [style.opacity]=\"isExpanded(i) ? 1 : 0\" [style.pointerEvents]=\"isExpanded(i) ? 'auto' : 'none'\">\n <div class=\"stepper\">\n <div class=\"step from-step\">\n <div class=\"dot from\"></div>\n <div class=\"content\">\n <div class=\"label\">De</div>\n <div class=\"value\">{{ showFriendly ? fromFriendly(c) : fromLabel(c) }}</div>\n </div>\n </div>\n <div class=\"step to-step\">\n <div class=\"dot to\"></div>\n <div class=\"content\">\n <div class=\"label\">Para</div>\n <div class=\"value\">{{ showFriendly ? toFriendly(c) : toLabel(c) }}</div>\n </div>\n </div>\n <div class=\"step map-step\" *ngIf=\"c.map\">\n <div class=\"dot map\"></div>\n <div class=\"content\">\n <div class=\"label\">Map</div>\n <div class=\"value mono\">{{ c.map }}</div>\n </div>\n </div>\n </div>\n </div>\n </mat-list-item>\n </ng-container>\n </div>\n </div>\n </ng-container>\n <ng-template #flatList>\n <ng-container *ngFor=\"let c of filteredConnections(); let i = index\">\n <mat-list-item role=\"listitem\" (click)=\"startEdit(i, c)\" [class.selected]=\"selectedIndex()===i\">\n <div class=\"card-head\" matListItemTitle [matTooltip]=\"showFriendly ? (c.from.widget + '.' + c.from.output + ' \u2192 ' + (c.to.widget + '.' + c.to.input)) : ''\">\n <mat-icon class=\"comp-icon from\" [matTooltip]=\"widgetFriendlyNameForKey(c.from.widget)\">{{ componentIconForKey(c.from.widget) }}</mat-icon>\n <span class=\"pdx-badge from\" aria-hidden=\"true\"></span>\n <span class=\"from\">{{ showFriendly ? fromFriendly(c) : fromLabel(c) }}</span>\n <span class=\"arrow\">\u2192</span>\n <mat-icon class=\"comp-icon to\" [matTooltip]=\"widgetFriendlyNameForKey(c.to.widget)\">{{ componentIconForKey(c.to.widget) }}</mat-icon>\n <span class=\"pdx-badge to\" aria-hidden=\"true\"></span>\n <span class=\"to\">{{ showFriendly ? toFriendly(c) : toLabel(c) }}</span>\n <span class=\"spacer\"></span>\n <span class=\"status-pill\" [class.ok]=\"connectionStatus(c)==='ok'\" [class.warn]=\"connectionStatus(c)==='warn'\" [class.err]=\"connectionStatus(c)==='err'\">\n <mat-icon *ngIf=\"connectionStatus(c)==='ok'\">check_circle</mat-icon>\n <mat-icon *ngIf=\"connectionStatus(c)==='warn'\">warning</mat-icon>\n <mat-icon *ngIf=\"connectionStatus(c)==='err'\">error</mat-icon>\n </span>\n <button mat-icon-button class=\"expand-btn\" [attr.aria-expanded]=\"isExpanded(i)\" (click)=\"toggleExpanded(i, $event)\" [matTooltip]=\"isExpanded(i) ? 'Recolher detalhes' : 'Expandir detalhes'\">\n <mat-icon>{{ isExpanded(i) ? 'expand_less' : 'expand_more' }}</mat-icon>\n </button>\n </div>\n <div class=\"meta\" *ngIf=\"showFriendly\" matListItemLine>\n <span class=\"hint\">{{ outputDescription(c) }}</span>\n <span class=\"sep\" *ngIf=\"outputDescription(c) && inputDescription(c)\">\u2022</span>\n <span class=\"hint\">{{ inputDescription(c) }}</span>\n </div>\n <div class=\"map-chip\" *ngIf=\"c.map\" [matTooltip]=\"c.map || ''\" matListItemLine (click)=\"startEditByConn(c)\" title=\"Clique para editar Map\">\n <span class=\"pdx-badge map\" aria-hidden=\"true\"></span>\n <mat-icon class=\"map-icon\" inline>bolt</mat-icon>\n <span class=\"mono\">{{ c.map }}</span>\n </div>\n <div class=\"card-actions\" matListItemLine (click)=\"$event.stopPropagation()\">\n <span class=\"action-group\">\n <button mat-icon-button (click)=\"startEdit(i, c)\" matTooltip=\"Editar\"><mat-icon>settings</mat-icon></button>\n <button mat-icon-button (click)=\"duplicateConnection(i)\" matTooltip=\"Duplicar\"><mat-icon>content_copy</mat-icon></button>\n </span>\n <span class=\"action-group\">\n <button mat-stroked-button color=\"primary\" (click)=\"openDiagramFor(c)\" matTooltip=\"Ver rela\u00E7\u00E3o no diagrama\"><mat-icon>schema</mat-icon><span>Diagrama</span></button>\n </span>\n <span class=\"spacer\"></span>\n <span class=\"action-group\">\n <button mat-icon-button color=\"warn\" (click)=\"removeConnection(i)\" matTooltip=\"Remover\"><mat-icon>delete</mat-icon></button>\n </span>\n </div>\n <div class=\"card-details\" matListItemLine [class.expanded]=\"isExpanded(i)\" [style.maxHeight]=\"isExpanded(i) ? '240px' : '0px'\" [style.opacity]=\"isExpanded(i) ? 1 : 0\" [style.pointerEvents]=\"isExpanded(i) ? 'auto' : 'none'\">\n <div class=\"stepper\">\n <div class=\"step from-step\">\n <div class=\"dot from\"></div>\n <div class=\"content\">\n <div class=\"label\">De</div>\n <div class=\"value\">{{ showFriendly ? fromFriendly(c) : fromLabel(c) }}</div>\n </div>\n </div>\n <div class=\"step to-step\">\n <div class=\"dot to\"></div>\n <div class=\"content\">\n <div class=\"label\">Para</div>\n <div class=\"value\">{{ showFriendly ? toFriendly(c) : toLabel(c) }}</div>\n </div>\n </div>\n <div class=\"step map-step\" *ngIf=\"c.map\">\n <div class=\"dot map\"></div>\n <div class=\"content\">\n <div class=\"label\">Map</div>\n <div class=\"value mono\">{{ c.map }}</div>\n </div>\n </div>\n </div>\n </div>\n </mat-list-item>\n </ng-container>\n </ng-template>\n </mat-list>\n <ng-template #noConns>\n <div class=\"pdx-empty-list\" role=\"status\" aria-live=\"polite\">\n <mat-icon>link_off</mat-icon>\n <div>Nenhuma conex\u00E3o definida. Use \"Nova conex\u00E3o\".</div>\n </div>\n </ng-template>\n </div>\n\n <!-- Right: editor form -->\n <div class=\"pdx-conn-editor\" role=\"form\" aria-label=\"Editor de conex\u00E3o\">\n <ng-container *ngIf=\"selectedIndex() >= 0; else emptyEditor\">\n <div class=\"form-grid\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Origem (widget.key)</mat-label>\n <input matInput [(ngModel)]=\"connections()[selectedIndex()].from.widget\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Evento (output)</mat-label>\n <input matInput [(ngModel)]=\"connections()[selectedIndex()].from.output\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Destino (widget.key)</mat-label>\n <input matInput [(ngModel)]=\"connections()[selectedIndex()].to.widget\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Input</mat-label>\n <input matInput [(ngModel)]=\"connections()[selectedIndex()].to.input\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"full-span\">\n <mat-label>Map (opcional)</mat-label>\n <input matInput [(ngModel)]=\"connections()[selectedIndex()].map\" [placeholder]=\"mapPlaceholder\" />\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n class=\"help-icon-button\"\n matTooltip=\"Ex.: payload.id ou ${payload.id}.\"\n >\n <mat-icon>help_outline</mat-icon>\n </button>\n </mat-form-field>\n <div class=\"full-span\">\n <button mat-flat-button color=\"primary\" (click)=\"onSave()\"><mat-icon [praxisIcon]=\"'save'\"></mat-icon>Salvar</button>\n </div>\n </div>\n </ng-container>\n <ng-template #emptyEditor>\n <div class=\"pdx-empty-editor\">Selecione uma conex\u00E3o para editar.</div>\n </ng-template>\n </div>\n </div>\n</div>\n", styles: [".pdx-conn-root{display:grid;gap:12px;color:var(--md-sys-color-on-surface)}.pdx-conn-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.pdx-conn-title{font-weight:600}.pdx-conn-count{color:var(--md-sys-color-on-surface-variant)}.pdx-conn-search{width:260px}.pdx-conn-select{width:160px}.pdx-conn-group{width:170px}.pdx-spacer{flex:1}.pdx-diagram-cta span{margin-left:6px}.pdx-conn-grid{display:grid;grid-template-columns:1.4fr 1fr;gap:12px}.pdx-conn-list{height:540px;overflow:auto;padding:8px 0;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;background:var(--md-sys-color-surface)}.pdx-conn-list .mat-mdc-list-item.mdc-list-item{height:auto;padding:10px 8px 8px}.pdx-conn-list .mat-mdc-list-item.selected{background:var(--md-sys-color-primary-container)}.card-head{display:flex;align-items:center;gap:6px}.card-head .spacer{flex:1}.card-head .comp-icon{opacity:.85}.card-head .expand-btn{margin-left:4px}.status-pill{display:inline-flex;align-items:center;gap:4px;border-radius:999px;padding:2px 8px}.status-pill.ok{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.status-pill.warn{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container)}.status-pill.err{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.map-chip{display:inline-flex;align-items:center;gap:6px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.map-chip .map-icon{font-size:16px}.pdx-conn-editor{min-height:540px;padding:8px;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;background:var(--md-sys-color-surface)}.pdx-empty-list{padding:16px;display:grid;place-items:center;gap:6px;opacity:.8}.pdx-empty-editor{padding:16px;opacity:.75}.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;align-content:start}.full-span{grid-column:1/-1}.stepper{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;padding:8px 0}.step{display:grid;grid-template-columns:12px 1fr;gap:8px;align-items:start}.dot{width:12px;height:12px;border-radius:50%;margin-top:5px}.dot.from{background:var(--md-sys-color-primary-container)}.dot.to{background:var(--md-sys-color-secondary-container)}.dot.map{background:var(--md-sys-color-tertiary-container)}.group-block{padding:6px 6px 10px}.group-header{display:flex;gap:8px;align-items:center;padding:8px 0}.group-title{font-weight:600}.group-count{opacity:.75}.group-list{display:grid;gap:6px}.pdx-badge{width:10px;height:10px;border-radius:50%;display:inline-block}.pdx-badge.from{background:var(--md-sys-color-primary-container)}.pdx-badge.to{background:var(--md-sys-color-secondary-container)}.pdx-badge.map{background:var(--md-sys-color-tertiary-container)}.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px}.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;vertical-align:middle}.help-icon-button mat-icon{font-size:18px;line-height:18px;width:18px;height:18px}@media(max-width:960px){.pdx-conn-grid{grid-template-columns:1fr}.pdx-conn-list,.pdx-conn-editor{min-height:360px}.pdx-conn-search,.pdx-conn-select,.pdx-conn-group{width:100%}}\n"] }]
|
|
749
|
+
args: [{ selector: 'praxis-connection-builder', standalone: true, imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule, MatSelectModule, MatButtonModule, MatIconModule, MatListModule, MatDialogModule, MatAutocompleteModule, MatMenuModule, MatTabsModule, MatTooltipModule, MatProgressBarModule, MatCheckboxModule, MatSnackBarModule, ScrollingModule, PraxisIconDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"pdx-conn-root\" role=\"region\" aria-label=\"Construtor de Conex\u00C3\u00B5es\">\n <div class=\"pdx-conn-head\">\n <span class=\"pdx-conn-title\">Conex\u00C3\u00B5es</span>\n <span class=\"pdx-conn-count\" aria-label=\"Conex\u00C3\u00B5es filtradas e total\">{{ filteredConnections().length }} / {{ connections().length }}</span>\n <mat-form-field appearance=\"outline\" class=\"pdx-conn-search\">\n <mat-label>Buscar</mat-label>\n <input matInput [(ngModel)]=\"filterText\" placeholder=\"Origem, Destino, Input, Map...\" />\n <button mat-icon-button matSuffix *ngIf=\"filterText\" (click)=\"filterText='';\" aria-label=\"Limpar busca\"><mat-icon [praxisIcon]=\"'close'\"></mat-icon></button>\n </mat-form-field>\n <span class=\"pdx-spacer\"></span>\n <button mat-button (click)=\"toggleShowOnlyIssues()\" [color]=\"showOnlyIssues ? 'primary': undefined\" aria-label=\"Somente avisos/erros\"><mat-icon [praxisIcon]=\"'report_problem'\"></mat-icon> Alertas</button>\n <mat-form-field appearance=\"outline\" class=\"pdx-conn-select\">\n <mat-label>Ordenar por</mat-label>\n <mat-select [(ngModel)]=\"sortBy\" (ngModelChange)=\"setSortBy($event)\">\n <mat-option value=\"from\">Origem</mat-option>\n <mat-option value=\"to\">Destino</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"pdx-conn-group\">\n <mat-label>Agrupar por</mat-label>\n <mat-select [(ngModel)]=\"groupBy\" (ngModelChange)=\"onGroupByChange()\">\n <mat-option value=\"none\">Nenhum</mat-option>\n <mat-option value=\"from\">Origem</mat-option>\n <mat-option value=\"to\">Destino</mat-option>\n <mat-option value=\"event\">Evento</mat-option>\n </mat-select>\n </mat-form-field>\n <button mat-icon-button [color]=\"showFriendly ? 'primary' : undefined\" (click)=\"toggleFriendly()\" aria-label=\"Alternar nome amig\u00C3\u00A1vel/t\u00C3\u00A9cnico\"><mat-icon [praxisIcon]=\"'translate'\"></mat-icon></button>\n <button mat-stroked-button (click)=\"createNew()\" aria-label=\"Nova conex\u00C3\u00A3o\"><mat-icon [praxisIcon]=\"'add'\"></mat-icon> Nova conex\u00C3\u00A3o</button>\n <button mat-flat-button color=\"accent\" class=\"pdx-diagram-cta\" (click)=\"openDiagramFullscreen()\" aria-label=\"Abrir diagrama em tela cheia\" matTooltip=\"Visualizar conex\u00C3\u00B5es em grafo\"><mat-icon [praxisIcon]=\"'schema'\"></mat-icon><span>Diagrama</span></button>\n </div>\n\n <div class=\"pdx-conn-grid\">\n <div class=\"pdx-conn-list\" role=\"list\" aria-label=\"Lista de conex\u00C3\u00B5es\" cdkScrollable>\n <mat-list *ngIf=\"filteredConnections().length; else noConns\">\n <ng-container *ngIf=\"groupBy!=='none'; else flatList\">\n <div class=\"group-block\" *ngFor=\"let g of groupedConnections(); let gi = index\">\n <div class=\"group-header\">\n <span class=\"group-title\">{{ g.label }}</span>\n <span class=\"group-count\">{{ g.list.length }}</span>\n </div>\n <mat-divider></mat-divider>\n <div class=\"group-list\">\n <ng-container *ngFor=\"let c of g.list; let i = index\">\n <mat-list-item role=\"listitem\" (click)=\"startEdit(i, c)\" [class.selected]=\"selectedIndex()===i\">\n <div class=\"card-head\" matListItemTitle [matTooltip]=\"fromTooltip(c)\">\n <mat-icon class=\"comp-icon from\" [matTooltip]=\"fromIconTooltip(c)\">{{ fromIcon(c) }}</mat-icon>\n <span class=\"pdx-badge from\" aria-hidden=\"true\"></span>\n <span class=\"from\">{{ showFriendly ? fromFriendly(c) : fromLabel(c) }}</span>\n <span class=\"arrow\">\u00E2\u2020\u2019</span>\n <mat-icon class=\"comp-icon to\" [matTooltip]=\"toIconTooltip(c)\">{{ toIcon(c) }}</mat-icon>\n <span class=\"pdx-badge to\" aria-hidden=\"true\"></span>\n <span class=\"to\">{{ showFriendly ? toFriendly(c) : toLabel(c) }}</span>\n <span class=\"spacer\"></span>\n <span class=\"status-pill\" [class.ok]=\"connectionStatus(c)==='ok'\" [class.warn]=\"connectionStatus(c)==='warn'\" [class.err]=\"connectionStatus(c)==='err'\">\n <mat-icon *ngIf=\"connectionStatus(c)==='ok'\">check_circle</mat-icon>\n <mat-icon *ngIf=\"connectionStatus(c)==='warn'\">warning</mat-icon>\n <mat-icon *ngIf=\"connectionStatus(c)==='err'\">error</mat-icon>\n </span>\n <button mat-icon-button class=\"expand-btn\" [attr.aria-expanded]=\"isExpanded(i)\" (click)=\"toggleExpanded(i, $event)\" [matTooltip]=\"isExpanded(i) ? 'Recolher detalhes' : 'Expandir detalhes'\">\n <mat-icon>{{ isExpanded(i) ? 'expand_less' : 'expand_more' }}</mat-icon>\n </button>\n </div>\n <div class=\"meta\" *ngIf=\"showFriendly\" matListItemLine>\n <span class=\"hint\">{{ outputDescription(c) }}</span>\n <span class=\"sep\" *ngIf=\"outputDescription(c) && inputDescription(c)\">\u00E2\u20AC\u00A2</span>\n <span class=\"hint\">{{ inputDescription(c) }}</span>\n </div>\n <div class=\"map-chip\" *ngIf=\"c.map\" [matTooltip]=\"c.map || ''\" matListItemLine (click)=\"startEditByConn(c)\" title=\"Clique para editar Map\">\n <span class=\"pdx-badge map\" aria-hidden=\"true\"></span>\n <mat-icon class=\"map-icon\" inline>bolt</mat-icon>\n <span class=\"mono\">{{ c.map }}</span>\n </div>\n <div class=\"card-actions\" matListItemLine (click)=\"$event.stopPropagation()\">\n <span class=\"action-group\">\n <button mat-icon-button (click)=\"startEdit(i, c)\" matTooltip=\"Editar\"><mat-icon>settings</mat-icon></button>\n <button mat-icon-button (click)=\"duplicateConnection(i)\" matTooltip=\"Duplicar\"><mat-icon>content_copy</mat-icon></button>\n </span>\n <span class=\"action-group\">\n <button mat-stroked-button color=\"primary\" (click)=\"openDiagramFor(c)\" matTooltip=\"Ver rela\u00C3\u00A7\u00C3\u00A3o no diagrama\"><mat-icon>schema</mat-icon><span>Diagrama</span></button>\n </span>\n <span class=\"spacer\"></span>\n <span class=\"action-group\">\n <button mat-icon-button color=\"warn\" (click)=\"removeConnection(i)\" matTooltip=\"Remover\"><mat-icon>delete</mat-icon></button>\n </span>\n </div>\n <div class=\"card-details\" matListItemLine [class.expanded]=\"isExpanded(i)\" [style.maxHeight]=\"isExpanded(i) ? '240px' : '0px'\" [style.opacity]=\"isExpanded(i) ? 1 : 0\" [style.pointerEvents]=\"isExpanded(i) ? 'auto' : 'none'\">\n <div class=\"stepper\">\n <div class=\"step from-step\">\n <div class=\"dot from\"></div>\n <div class=\"content\">\n <div class=\"label\">De</div>\n <div class=\"value\">{{ showFriendly ? fromFriendly(c) : fromLabel(c) }}</div>\n </div>\n </div>\n <div class=\"step to-step\">\n <div class=\"dot to\"></div>\n <div class=\"content\">\n <div class=\"label\">Para</div>\n <div class=\"value\">{{ showFriendly ? toFriendly(c) : toLabel(c) }}</div>\n </div>\n </div>\n <div class=\"step map-step\" *ngIf=\"c.map\">\n <div class=\"dot map\"></div>\n <div class=\"content\">\n <div class=\"label\">Map</div>\n <div class=\"value mono\">{{ c.map }}</div>\n </div>\n </div>\n </div>\n </div>\n </mat-list-item>\n </ng-container>\n </div>\n </div>\n </ng-container>\n <ng-template #flatList>\n <ng-container *ngFor=\"let c of filteredConnections(); let i = index\">\n <mat-list-item role=\"listitem\" (click)=\"startEdit(i, c)\" [class.selected]=\"selectedIndex()===i\">\n <div class=\"card-head\" matListItemTitle [matTooltip]=\"fromTooltip(c)\">\n <mat-icon class=\"comp-icon from\" [matTooltip]=\"fromIconTooltip(c)\">{{ fromIcon(c) }}</mat-icon>\n <span class=\"pdx-badge from\" aria-hidden=\"true\"></span>\n <span class=\"from\">{{ showFriendly ? fromFriendly(c) : fromLabel(c) }}</span>\n <span class=\"arrow\">\u00E2\u2020\u2019</span>\n <mat-icon class=\"comp-icon to\" [matTooltip]=\"toIconTooltip(c)\">{{ toIcon(c) }}</mat-icon>\n <span class=\"pdx-badge to\" aria-hidden=\"true\"></span>\n <span class=\"to\">{{ showFriendly ? toFriendly(c) : toLabel(c) }}</span>\n <span class=\"spacer\"></span>\n <span class=\"status-pill\" [class.ok]=\"connectionStatus(c)==='ok'\" [class.warn]=\"connectionStatus(c)==='warn'\" [class.err]=\"connectionStatus(c)==='err'\">\n <mat-icon *ngIf=\"connectionStatus(c)==='ok'\">check_circle</mat-icon>\n <mat-icon *ngIf=\"connectionStatus(c)==='warn'\">warning</mat-icon>\n <mat-icon *ngIf=\"connectionStatus(c)==='err'\">error</mat-icon>\n </span>\n <button mat-icon-button class=\"expand-btn\" [attr.aria-expanded]=\"isExpanded(i)\" (click)=\"toggleExpanded(i, $event)\" [matTooltip]=\"isExpanded(i) ? 'Recolher detalhes' : 'Expandir detalhes'\">\n <mat-icon>{{ isExpanded(i) ? 'expand_less' : 'expand_more' }}</mat-icon>\n </button>\n </div>\n <div class=\"meta\" *ngIf=\"showFriendly\" matListItemLine>\n <span class=\"hint\">{{ outputDescription(c) }}</span>\n <span class=\"sep\" *ngIf=\"outputDescription(c) && inputDescription(c)\">\u00E2\u20AC\u00A2</span>\n <span class=\"hint\">{{ inputDescription(c) }}</span>\n </div>\n <div class=\"map-chip\" *ngIf=\"c.map\" [matTooltip]=\"c.map || ''\" matListItemLine (click)=\"startEditByConn(c)\" title=\"Clique para editar Map\">\n <span class=\"pdx-badge map\" aria-hidden=\"true\"></span>\n <mat-icon class=\"map-icon\" inline>bolt</mat-icon>\n <span class=\"mono\">{{ c.map }}</span>\n </div>\n <div class=\"card-actions\" matListItemLine (click)=\"$event.stopPropagation()\">\n <span class=\"action-group\">\n <button mat-icon-button (click)=\"startEdit(i, c)\" matTooltip=\"Editar\"><mat-icon>settings</mat-icon></button>\n <button mat-icon-button (click)=\"duplicateConnection(i)\" matTooltip=\"Duplicar\"><mat-icon>content_copy</mat-icon></button>\n </span>\n <span class=\"action-group\">\n <button mat-stroked-button color=\"primary\" (click)=\"openDiagramFor(c)\" matTooltip=\"Ver rela\u00C3\u00A7\u00C3\u00A3o no diagrama\"><mat-icon>schema</mat-icon><span>Diagrama</span></button>\n </span>\n <span class=\"spacer\"></span>\n <span class=\"action-group\">\n <button mat-icon-button color=\"warn\" (click)=\"removeConnection(i)\" matTooltip=\"Remover\"><mat-icon>delete</mat-icon></button>\n </span>\n </div>\n <div class=\"card-details\" matListItemLine [class.expanded]=\"isExpanded(i)\" [style.maxHeight]=\"isExpanded(i) ? '240px' : '0px'\" [style.opacity]=\"isExpanded(i) ? 1 : 0\" [style.pointerEvents]=\"isExpanded(i) ? 'auto' : 'none'\">\n <div class=\"stepper\">\n <div class=\"step from-step\">\n <div class=\"dot from\"></div>\n <div class=\"content\">\n <div class=\"label\">De</div>\n <div class=\"value\">{{ showFriendly ? fromFriendly(c) : fromLabel(c) }}</div>\n </div>\n </div>\n <div class=\"step to-step\">\n <div class=\"dot to\"></div>\n <div class=\"content\">\n <div class=\"label\">Para</div>\n <div class=\"value\">{{ showFriendly ? toFriendly(c) : toLabel(c) }}</div>\n </div>\n </div>\n <div class=\"step map-step\" *ngIf=\"c.map\">\n <div class=\"dot map\"></div>\n <div class=\"content\">\n <div class=\"label\">Map</div>\n <div class=\"value mono\">{{ c.map }}</div>\n </div>\n </div>\n </div>\n </div>\n </mat-list-item>\n </ng-container>\n </ng-template>\n </mat-list>\n <ng-template #noConns>\n <div class=\"pdx-empty-list\" role=\"status\" aria-live=\"polite\">\n <mat-icon>link_off</mat-icon>\n <div>Nenhuma conex\u00C3\u00A3o definida. Use \"Nova conex\u00C3\u00A3o\".</div>\n </div>\n </ng-template>\n </div>\n\n <div class=\"pdx-conn-editor\" role=\"form\" aria-label=\"Editor de conex\u00C3\u00A3o\">\n <ng-container *ngIf=\"selectedIndex() >= 0; else emptyEditor\">\n <div class=\"form-grid\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Origem (widget.key)</mat-label>\n <input matInput [ngModel]=\"selectedSourcePrimaryValue()\" (ngModelChange)=\"setSelectedSourcePrimaryValue($event)\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" *ngIf=\"showSelectedSourceSecondary()\">\n <mat-label>Evento (output)</mat-label>\n <input matInput [ngModel]=\"selectedSourceSecondaryValue()\" (ngModelChange)=\"setSelectedSourceSecondaryValue($event)\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Destino (widget.key)</mat-label>\n <input matInput [ngModel]=\"selectedTargetPrimaryValue()\" (ngModelChange)=\"setSelectedTargetPrimaryValue($event)\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" *ngIf=\"showSelectedTargetSecondary()\">\n <mat-label>Input</mat-label>\n <input matInput [ngModel]=\"selectedTargetSecondaryValue()\" (ngModelChange)=\"setSelectedTargetSecondaryValue($event)\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"full-span\">\n <mat-label>Map (opcional)</mat-label>\n <input matInput [ngModel]=\"selectedMapValue()\" (ngModelChange)=\"setSelectedMapValue($event)\" [placeholder]=\"mapPlaceholder\" />\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n class=\"help-icon-button\"\n matTooltip=\"Ex.: payload.id ou ${payload.id}.\"\n >\n <mat-icon>help_outline</mat-icon>\n </button>\n </mat-form-field>\n <div class=\"full-span\">\n <button mat-flat-button color=\"primary\" (click)=\"onSave()\"><mat-icon [praxisIcon]=\"'save'\"></mat-icon>Salvar</button>\n </div>\n </div>\n </ng-container>\n <ng-template #emptyEditor>\n <div class=\"pdx-empty-editor\">Selecione uma conex\u00C3\u00A3o para editar.</div>\n </ng-template>\n </div>\n </div>\n</div>\n", styles: [".pdx-conn-root{display:grid;gap:12px;color:var(--md-sys-color-on-surface)}.pdx-conn-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.pdx-conn-title{font-weight:600}.pdx-conn-count{color:var(--md-sys-color-on-surface-variant)}.pdx-conn-search{width:260px}.pdx-conn-select{width:160px}.pdx-conn-group{width:170px}.pdx-spacer{flex:1}.pdx-diagram-cta span{margin-left:6px}.pdx-conn-grid{display:grid;grid-template-columns:1.4fr 1fr;gap:12px}.pdx-conn-list{height:540px;overflow:auto;padding:8px 0;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;background:var(--md-sys-color-surface)}.pdx-conn-list .mat-mdc-list-item.mdc-list-item{height:auto;padding:10px 8px 8px}.pdx-conn-list .mat-mdc-list-item.selected{background:var(--md-sys-color-primary-container)}.card-head{display:flex;align-items:center;gap:6px}.card-head .spacer{flex:1}.card-head .comp-icon{opacity:.85}.card-head .expand-btn{margin-left:4px}.status-pill{display:inline-flex;align-items:center;gap:4px;border-radius:999px;padding:2px 8px}.status-pill.ok{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.status-pill.warn{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container)}.status-pill.err{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.map-chip{display:inline-flex;align-items:center;gap:6px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.map-chip .map-icon{font-size:16px}.pdx-conn-editor{min-height:540px;padding:8px;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;background:var(--md-sys-color-surface)}.pdx-empty-list{padding:16px;display:grid;place-items:center;gap:6px;opacity:.8}.pdx-empty-editor{padding:16px;opacity:.75}.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;align-content:start}.full-span{grid-column:1/-1}.stepper{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;padding:8px 0}.step{display:grid;grid-template-columns:12px 1fr;gap:8px;align-items:start}.dot{width:12px;height:12px;border-radius:50%;margin-top:5px}.dot.from{background:var(--md-sys-color-primary-container)}.dot.to{background:var(--md-sys-color-secondary-container)}.dot.map{background:var(--md-sys-color-tertiary-container)}.group-block{padding:6px 6px 10px}.group-header{display:flex;gap:8px;align-items:center;padding:8px 0}.group-title{font-weight:600}.group-count{opacity:.75}.group-list{display:grid;gap:6px}.pdx-badge{width:10px;height:10px;border-radius:50%;display:inline-block}.pdx-badge.from{background:var(--md-sys-color-primary-container)}.pdx-badge.to{background:var(--md-sys-color-secondary-container)}.pdx-badge.map{background:var(--md-sys-color-tertiary-container)}.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px}.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;vertical-align:middle}.help-icon-button mat-icon{font-size:18px;line-height:18px;width:18px;height:18px}@media(max-width:960px){.pdx-conn-grid{grid-template-columns:1fr}.pdx-conn-list,.pdx-conn-editor{min-height:360px}.pdx-conn-search,.pdx-conn-select,.pdx-conn-group{width:100%}}\n"] }]
|
|
629
750
|
}], ctorParameters: () => [{ type: i1.MatDialog }, { type: i1$1.ComponentMetadataRegistry }, { type: i3$1.MatSnackBar }], propDecorators: { page: [{
|
|
630
751
|
type: Input
|
|
631
752
|
}], widgets: [{
|
|
@@ -1729,7 +1850,7 @@ class WidgetShellEditorComponent {
|
|
|
1729
1850
|
<div class="shell-empty">Nenhuma ação configurada.</div>
|
|
1730
1851
|
</ng-template>
|
|
1731
1852
|
</div>
|
|
1732
|
-
`, isInline: true, styles: [".shell-editor{display:grid;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.shell-head{display:flex;align-items:center;gap:12px}.shell-title{font-weight:600}.shell-subtitle{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-section{font-weight:600;margin-top:8px}.shell-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.shell-flags{display:flex;gap:16px;align-items:center}.shell-actions-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.shell-available{display:grid;gap:12px}.shell-available-toolbar{display:grid;gap:12px;grid-template-columns:minmax(200px,1fr)}.shell-available-filters{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.shell-available-list{display:grid;gap:10px}.shell-available-item{display:grid;gap:8px;grid-template-columns:1fr auto;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;padding:12px;background:var(--md-sys-color-surface-container-low)}.shell-available-title{display:flex;align-items:center;gap:10px}.shell-available-text{display:grid;gap:4px}.shell-available-label{font-weight:600}.shell-available-meta{display:flex;gap:8px;flex-wrap:wrap;font-size:11px;color:var(--md-sys-color-on-surface-variant)}.shell-available-desc{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-available-payload{font-size:11px;color:var(--md-sys-color-on-surface-variant)}.shell-available-warning{font-size:11px;color:var(--md-sys-color-error)}.shell-available-actions{display:flex;align-items:flex-start;justify-content:flex-end}.shell-badge{padding:2px 6px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);font-size:10px}.shell-code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.shell-actions{display:grid;gap:12px}.shell-action{border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;padding:12px;display:grid;gap:8px}.shell-action-row{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.shell-action-actions{display:flex;justify-content:flex-end}.shell-action-hint,.shell-empty{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-hint{align-self:center;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-quick-inputs{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));align-items:end}.shell-input-path{font-size:12px;color:var(--md-sys-color-on-surface-variant);align-self:center}.shell-editor .mat-mdc-form-field{width:100%}.shell-preview{border:1px dashed var(--md-sys-color-outline-variant);border-radius:12px;padding:12px}.shell-preview-card{background:var(--pdx-shell-card-bg, var(--md-sys-color-surface));border:1px solid var(--pdx-shell-card-border, var(--md-sys-color-outline-variant));border-radius:var(--pdx-shell-card-radius, 14px);box-shadow:var(--pdx-shell-card-shadow, var(--mat-elevation-level2));overflow:hidden}.shell-preview-card.no-shell{background:transparent;border:none;box-shadow:none}.shell-preview-card.expanded{box-shadow:var(--pdx-shell-card-shadow, 0 16px 34px rgba(0,0,0,.32));transform:scale(1.01)}.shell-preview-header{display:flex;align-items:center;gap:12px;padding:10px 12px 8px;border-bottom:1px solid var(--pdx-shell-header-border, var(--md-sys-color-outline-variant));background:var(--pdx-shell-header-bg, var(--md-sys-color-surface-container))}.shell-preview-title{display:flex;align-items:center;gap:10px;min-width:0;flex:1;color:var(--pdx-shell-title-color, inherit)}.shell-preview-title mat-icon{color:var(--pdx-shell-icon-color, currentColor)}.shell-preview-text{min-width:0}.shell-preview-title-text{font-weight:var(--pdx-shell-title-weight, 600);font-size:var(--pdx-shell-title-size, 14px);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shell-preview-subtitle{font-size:var(--pdx-shell-subtitle-size, 12px);opacity:.75;color:var(--pdx-shell-subtitle-color, currentColor);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shell-preview-actions{display:flex;align-items:center;gap:6px}.pdx-preview-text{padding:0 8px}.pdx-preview-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-preview-label{font-size:12px;font-weight:500}.shell-preview-body{padding:var(--pdx-shell-body-padding, 10px 12px 12px 12px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit);font-size:12px}.shell-preview-body.hidden{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i5.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: MatDividerModule }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.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: "ngmodule", type: MatSelectModule }, { kind: "component", type:
|
|
1853
|
+
`, isInline: true, styles: [".shell-editor{display:grid;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.shell-head{display:flex;align-items:center;gap:12px}.shell-title{font-weight:600}.shell-subtitle{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-section{font-weight:600;margin-top:8px}.shell-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.shell-flags{display:flex;gap:16px;align-items:center}.shell-actions-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.shell-available{display:grid;gap:12px}.shell-available-toolbar{display:grid;gap:12px;grid-template-columns:minmax(200px,1fr)}.shell-available-filters{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.shell-available-list{display:grid;gap:10px}.shell-available-item{display:grid;gap:8px;grid-template-columns:1fr auto;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;padding:12px;background:var(--md-sys-color-surface-container-low)}.shell-available-title{display:flex;align-items:center;gap:10px}.shell-available-text{display:grid;gap:4px}.shell-available-label{font-weight:600}.shell-available-meta{display:flex;gap:8px;flex-wrap:wrap;font-size:11px;color:var(--md-sys-color-on-surface-variant)}.shell-available-desc{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-available-payload{font-size:11px;color:var(--md-sys-color-on-surface-variant)}.shell-available-warning{font-size:11px;color:var(--md-sys-color-error)}.shell-available-actions{display:flex;align-items:flex-start;justify-content:flex-end}.shell-badge{padding:2px 6px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);font-size:10px}.shell-code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.shell-actions{display:grid;gap:12px}.shell-action{border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;padding:12px;display:grid;gap:8px}.shell-action-row{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.shell-action-actions{display:flex;justify-content:flex-end}.shell-action-hint,.shell-empty{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-hint{align-self:center;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-quick-inputs{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));align-items:end}.shell-input-path{font-size:12px;color:var(--md-sys-color-on-surface-variant);align-self:center}.shell-editor .mat-mdc-form-field{width:100%}.shell-preview{border:1px dashed var(--md-sys-color-outline-variant);border-radius:12px;padding:12px}.shell-preview-card{background:var(--pdx-shell-card-bg, var(--md-sys-color-surface));border:1px solid var(--pdx-shell-card-border, var(--md-sys-color-outline-variant));border-radius:var(--pdx-shell-card-radius, 14px);box-shadow:var(--pdx-shell-card-shadow, var(--mat-elevation-level2));overflow:hidden}.shell-preview-card.no-shell{background:transparent;border:none;box-shadow:none}.shell-preview-card.expanded{box-shadow:var(--pdx-shell-card-shadow, 0 16px 34px rgba(0,0,0,.32));transform:scale(1.01)}.shell-preview-header{display:flex;align-items:center;gap:12px;padding:10px 12px 8px;border-bottom:1px solid var(--pdx-shell-header-border, var(--md-sys-color-outline-variant));background:var(--pdx-shell-header-bg, var(--md-sys-color-surface-container))}.shell-preview-title{display:flex;align-items:center;gap:10px;min-width:0;flex:1;color:var(--pdx-shell-title-color, inherit)}.shell-preview-title mat-icon{color:var(--pdx-shell-icon-color, currentColor)}.shell-preview-text{min-width:0}.shell-preview-title-text{font-weight:var(--pdx-shell-title-weight, 600);font-size:var(--pdx-shell-title-size, 14px);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shell-preview-subtitle{font-size:var(--pdx-shell-subtitle-size, 12px);opacity:.75;color:var(--pdx-shell-subtitle-color, currentColor);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shell-preview-actions{display:flex;align-items:center;gap:6px}.pdx-preview-text{padding:0 8px}.pdx-preview-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-preview-label{font-size:12px;font-weight:500}.shell-preview-body{padding:var(--pdx-shell-body-padding, 10px 12px 12px 12px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit);font-size:12px}.shell-preview-body.hidden{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i5.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: MatDividerModule }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.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: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.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: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1733
1854
|
}
|
|
1734
1855
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: WidgetShellEditorComponent, decorators: [{
|
|
1735
1856
|
type: Component,
|
|
@@ -2421,7 +2542,7 @@ class DynamicPageConfigEditorComponent {
|
|
|
2421
2542
|
</mat-tab>
|
|
2422
2543
|
</mat-tab-group>
|
|
2423
2544
|
</div>
|
|
2424
|
-
`, isInline: true, styles: [".page-config{display:flex;flex-direction:column;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;height:100%;box-sizing:border-box}.page-head{display:flex;align-items:center;gap:12px;flex-shrink:0}.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}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.page-title{font-weight:600}.spacer{flex:1}.page-section{font-weight:600;margin-top:8px}.page-form{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.page-config .mat-mdc-form-field{width:100%}.config-tabs{flex:1;min-height:0;display:flex;flex-direction:column}::ng-deep .config-tabs .mat-mdc-tab-body-wrapper{flex:1;min-height:0;display:flex;flex-direction:column}::ng-deep .mat-mdc-tab-body,::ng-deep .mat-mdc-tab-body-content{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.tab-content{padding-top:16px;display:flex;flex-direction:column;gap:12px;height:100%;box-sizing:border-box;overflow-y:auto}.json-editor-container{display:flex;flex-direction:column;height:100%;flex:1;gap:8px;overflow:hidden}.json-toolbar{display:flex;align-items:center;gap:8px;flex-shrink:0}.json-editor-wrapper{flex:1;width:100%;height:100%;border:1px solid var(--md-sys-color-outline-variant);border-radius:4px;overflow:hidden;background-color:var(--md-sys-color-surface)}.json-editor-wrapper textarea{width:100%;height:100%;border:none;outline:none;resize:none;padding:12px;box-sizing:border-box;font-family:Monaco,Menlo,Ubuntu Mono,Consolas,monospace;font-size:13px;line-height:1.4;background:transparent;color:var(--md-sys-color-on-surface);white-space:pre}.json-editor-wrapper textarea:focus{background-color:var(--md-sys-color-surface-container-low)}.json-error{color:var(--md-sys-color-error);font-size:12px;padding:0 8px;flex-shrink:0}.validation-status{font-size:12px;font-weight:500;padding:4px 8px;border-radius:4px}.validation-status.valid{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container)}.validation-status.invalid{color:var(--md-sys-color-error);background:var(--md-sys-color-error-container)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.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: "ngmodule", type: MatSelectModule }, { kind: "component", type:
|
|
2545
|
+
`, isInline: true, styles: [".page-config{display:flex;flex-direction:column;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;height:100%;box-sizing:border-box}.page-head{display:flex;align-items:center;gap:12px;flex-shrink:0}.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}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.page-title{font-weight:600}.spacer{flex:1}.page-section{font-weight:600;margin-top:8px}.page-form{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.page-config .mat-mdc-form-field{width:100%}.config-tabs{flex:1;min-height:0;display:flex;flex-direction:column}::ng-deep .config-tabs .mat-mdc-tab-body-wrapper{flex:1;min-height:0;display:flex;flex-direction:column}::ng-deep .mat-mdc-tab-body,::ng-deep .mat-mdc-tab-body-content{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.tab-content{padding-top:16px;display:flex;flex-direction:column;gap:12px;height:100%;box-sizing:border-box;overflow-y:auto}.json-editor-container{display:flex;flex-direction:column;height:100%;flex:1;gap:8px;overflow:hidden}.json-toolbar{display:flex;align-items:center;gap:8px;flex-shrink:0}.json-editor-wrapper{flex:1;width:100%;height:100%;border:1px solid var(--md-sys-color-outline-variant);border-radius:4px;overflow:hidden;background-color:var(--md-sys-color-surface)}.json-editor-wrapper textarea{width:100%;height:100%;border:none;outline:none;resize:none;padding:12px;box-sizing:border-box;font-family:Monaco,Menlo,Ubuntu Mono,Consolas,monospace;font-size:13px;line-height:1.4;background:transparent;color:var(--md-sys-color-on-surface);white-space:pre}.json-editor-wrapper textarea:focus{background-color:var(--md-sys-color-surface-container-low)}.json-error{color:var(--md-sys-color-error);font-size:12px;padding:0 8px;flex-shrink:0}.validation-status{font-size:12px;font-weight:500;padding:4px 8px;border-radius:4px}.validation-status.valid{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container)}.validation-status.invalid{color:var(--md-sys-color-error);background:var(--md-sys-color-error-container)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.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: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.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: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i9.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i9.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2425
2546
|
}
|
|
2426
2547
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicPageConfigEditorComponent, decorators: [{
|
|
2427
2548
|
type: Component,
|
|
@@ -2555,7 +2676,7 @@ const PAGE_BUILDER_AI_CAPABILITIES = {
|
|
|
2555
2676
|
'praxis-dynamic-gridster-page',
|
|
2556
2677
|
],
|
|
2557
2678
|
notes: [
|
|
2558
|
-
'page segue o modelo GridPageDefinition (widgets, connections, options, context).',
|
|
2679
|
+
'page segue o modelo GridPageDefinition (widgets, connections, options, context e state opcional).',
|
|
2559
2680
|
'pageIdentity identifica o escopo de persistência e não pertence ao objeto page.',
|
|
2560
2681
|
],
|
|
2561
2682
|
capabilities: [
|
|
@@ -2577,6 +2698,11 @@ const PAGE_BUILDER_AI_CAPABILITIES = {
|
|
|
2577
2698
|
{ path: 'page.options.cols', category: 'layout', valueKind: 'number', description: 'Numero de colunas do grid.' },
|
|
2578
2699
|
{ path: 'page.options.rowHeight', category: 'layout', valueKind: 'number', description: 'Altura das linhas (px).' },
|
|
2579
2700
|
{ path: 'page.options.gap', category: 'layout', valueKind: 'number', description: 'Espacamento entre widgets (px).' },
|
|
2701
|
+
// --- Page state ---
|
|
2702
|
+
{ path: 'page.state', category: 'state', valueKind: 'object', description: 'Estado declarativo opcional da pagina.' },
|
|
2703
|
+
{ path: 'page.state.values', category: 'state', valueKind: 'object', description: 'Valores primarios persistiveis do estado da pagina.' },
|
|
2704
|
+
{ path: 'page.state.schema', category: 'state', valueKind: 'object', description: 'Descritores dos paths de estado primario.' },
|
|
2705
|
+
{ path: 'page.state.derived', category: 'state', valueKind: 'object', description: 'Descritores de estado derivado recomputado pelo runtime.' },
|
|
2580
2706
|
// --- Widgets ---
|
|
2581
2707
|
{ path: 'page.widgets', category: 'widgets', valueKind: 'array', description: 'Widgets da página.' },
|
|
2582
2708
|
{ path: 'page.widgets[].key', category: 'widgets', valueKind: 'string', description: 'ID unico do widget.', critical: true },
|
|
@@ -2595,10 +2721,12 @@ const PAGE_BUILDER_AI_CAPABILITIES = {
|
|
|
2595
2721
|
{ path: 'page.connections[].from', category: 'connections', valueKind: 'object', description: 'Origem da conexão.' },
|
|
2596
2722
|
{ path: 'page.connections[].from.widget', category: 'connections', valueKind: 'string', description: 'Widget origem (key).' },
|
|
2597
2723
|
{ path: 'page.connections[].from.output', category: 'connections', valueKind: 'string', description: 'Output do widget origem.' },
|
|
2724
|
+
{ path: 'page.connections[].from.state', category: 'connections', valueKind: 'string', description: 'Path de state origem quando a conexao parte do estado da pagina.' },
|
|
2598
2725
|
{ path: 'page.connections[].to', category: 'connections', valueKind: 'object', description: 'Destino da conexão.' },
|
|
2599
2726
|
{ path: 'page.connections[].to.widget', category: 'connections', valueKind: 'string', description: 'Widget destino (key).' },
|
|
2600
2727
|
{ path: 'page.connections[].to.input', category: 'connections', valueKind: 'string', description: 'Input destino (path).' },
|
|
2601
2728
|
{ path: 'page.connections[].to.bindingOrder', category: 'connections', valueKind: 'array', description: 'Ordem de aplicação dos inputs.' },
|
|
2729
|
+
{ path: 'page.connections[].to.state', category: 'connections', valueKind: 'string', description: 'Path de state destino quando a conexao escreve no estado da pagina.' },
|
|
2602
2730
|
{ path: 'page.connections[].map', category: 'connections', valueKind: 'expression', description: 'Mapeamento do payload.' },
|
|
2603
2731
|
{ path: 'page.connections[].set', category: 'connections', valueKind: 'object', description: 'Overrides fixos aplicados ao destino.' },
|
|
2604
2732
|
{ path: 'page.connections[].meta', category: 'connections', valueKind: 'object', description: 'Metadados opcionais para operadores de runtime.' },
|
|
@@ -2906,6 +3034,12 @@ class GraphMapperService {
|
|
|
2906
3034
|
constructor(registry) {
|
|
2907
3035
|
this.registry = registry;
|
|
2908
3036
|
}
|
|
3037
|
+
isWidgetSource(from) {
|
|
3038
|
+
return 'widget' in from;
|
|
3039
|
+
}
|
|
3040
|
+
isWidgetTarget(to) {
|
|
3041
|
+
return 'widget' in to;
|
|
3042
|
+
}
|
|
2909
3043
|
/** Build nodes and edges for a given page definition and widgets. */
|
|
2910
3044
|
mapToGraph(page, widgets) {
|
|
2911
3045
|
const nodes = [];
|
|
@@ -2918,6 +3052,7 @@ class GraphMapperService {
|
|
|
2918
3052
|
const gap = 28;
|
|
2919
3053
|
let leftY = 0;
|
|
2920
3054
|
let rightY = 0;
|
|
3055
|
+
const stateNodeIds = new Map();
|
|
2921
3056
|
// First pass: create a node per widget
|
|
2922
3057
|
for (const w of widgets) {
|
|
2923
3058
|
const meta = this.registry.get(w.definition.id);
|
|
@@ -3055,14 +3190,25 @@ class GraphMapperService {
|
|
|
3055
3190
|
const conns = (page?.connections || []);
|
|
3056
3191
|
for (let i = 0; i < conns.length; i++) {
|
|
3057
3192
|
const c = conns[i];
|
|
3058
|
-
const
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
const
|
|
3193
|
+
const fromState = !this.isWidgetSource(c.from)
|
|
3194
|
+
? this.ensureStateNode(stateNodeIds, nodeById, nodes, page, c.from.state, 'output', colWidth, gap, leftY)
|
|
3195
|
+
: null;
|
|
3196
|
+
const fromNodeId = this.isWidgetSource(c.from) ? c.from.widget : fromState.nodeId;
|
|
3197
|
+
const fromPortId = this.isWidgetSource(c.from) ? `out:${c.from.output}` : 'out:state';
|
|
3198
|
+
const toState = !this.isWidgetTarget(c.to)
|
|
3199
|
+
? this.ensureStateNode(stateNodeIds, nodeById, nodes, page, c.to.state, 'input', colWidth, gap, rightY)
|
|
3200
|
+
: null;
|
|
3201
|
+
let toNodeId = this.isWidgetTarget(c.to) ? c.to.widget : toState.nodeId;
|
|
3202
|
+
let toPortId = this.isWidgetTarget(c.to) ? `in:${c.to.input}` : 'in:state';
|
|
3203
|
+
if (fromState?.created)
|
|
3204
|
+
leftY += rowHeight + gap;
|
|
3205
|
+
if (toState?.created)
|
|
3206
|
+
rightY += rowHeight + gap;
|
|
3207
|
+
const deep = this.isWidgetTarget(c.to) ? this.parseTabsPath(c.to.input) : null;
|
|
3063
3208
|
let friendlyPath;
|
|
3064
|
-
if (deep) {
|
|
3065
|
-
const
|
|
3209
|
+
if (deep && this.isWidgetTarget(c.to)) {
|
|
3210
|
+
const target = c.to;
|
|
3211
|
+
const childId = `${target.widget}#${deep.kind}[${deep.index}]/${deep.widgetIndex}`;
|
|
3066
3212
|
// If such child exists, route edge to it and its input name
|
|
3067
3213
|
if (nodes.some((n) => n.id === childId)) {
|
|
3068
3214
|
toNodeId = childId;
|
|
@@ -3072,7 +3218,7 @@ class GraphMapperService {
|
|
|
3072
3218
|
// Keep to widget but still expose label/path
|
|
3073
3219
|
toPortId = `in:${deep.input}`;
|
|
3074
3220
|
}
|
|
3075
|
-
const cfg = tabsCfgByKey.get(
|
|
3221
|
+
const cfg = tabsCfgByKey.get(target.widget);
|
|
3076
3222
|
const labelFromCfg = deep.kind === 'group' ? cfg?.tabs?.[deep.index]?.textLabel : cfg?.nav?.links?.[deep.index]?.label;
|
|
3077
3223
|
const containerLabel = labelFromCfg ? (deep.kind === 'group' ? `Aba: ${labelFromCfg}` : `Link: ${labelFromCfg}`) : (deep.kind === 'group' ? `Aba ${deep.index + 1}` : `Link ${deep.index + 1}`);
|
|
3078
3224
|
const child = nodes.find(n => n.id === childId);
|
|
@@ -3086,12 +3232,50 @@ class GraphMapperService {
|
|
|
3086
3232
|
from: { nodeId: fromNodeId, portId: fromPortId },
|
|
3087
3233
|
to: { nodeId: toNodeId, portId: toPortId },
|
|
3088
3234
|
label: c.map || undefined,
|
|
3089
|
-
meta: { map: c.map, bindingOrder: c.to.bindingOrder, ...(friendlyPath ? { friendlyPath } : {}) },
|
|
3235
|
+
meta: { map: c.map, bindingOrder: this.isWidgetTarget(c.to) ? c.to.bindingOrder : undefined, ...(friendlyPath ? { friendlyPath } : {}) },
|
|
3090
3236
|
};
|
|
3091
3237
|
edges.push(edge);
|
|
3092
3238
|
}
|
|
3093
3239
|
return { nodes, edges };
|
|
3094
3240
|
}
|
|
3241
|
+
ensureStateNode(stateNodeIds, nodeById, nodes, page, statePath, kind, colWidth, gap, offsetY) {
|
|
3242
|
+
const key = `${kind}:${statePath}`;
|
|
3243
|
+
const existing = stateNodeIds.get(key);
|
|
3244
|
+
if (existing)
|
|
3245
|
+
return { nodeId: existing, created: false };
|
|
3246
|
+
const nodeId = `state:${kind}:${encodeURIComponent(statePath)}`;
|
|
3247
|
+
const portId = kind === 'output' ? 'out:state' : 'in:state';
|
|
3248
|
+
const x = kind === 'output' ? gap : colWidth + gap * 3;
|
|
3249
|
+
const structuredState = this.normalizeState(page?.state);
|
|
3250
|
+
const isDerived = !!structuredState?.derived?.[statePath];
|
|
3251
|
+
const node = {
|
|
3252
|
+
id: nodeId,
|
|
3253
|
+
label: `${isDerived ? 'derived' : 'state'}:${statePath}`,
|
|
3254
|
+
icon: isDerived ? 'function' : 'account_tree',
|
|
3255
|
+
type: isDerived ? 'page-derived-state' : 'page-state',
|
|
3256
|
+
parentId: null,
|
|
3257
|
+
collapsed: true,
|
|
3258
|
+
bounds: { x, y: gap + offsetY, width: colWidth, height: 72 },
|
|
3259
|
+
ports: [{
|
|
3260
|
+
id: portId,
|
|
3261
|
+
label: statePath,
|
|
3262
|
+
description: statePath,
|
|
3263
|
+
kind,
|
|
3264
|
+
anchor: { x: kind === 'output' ? x : x + colWidth, y: gap + offsetY + 24 },
|
|
3265
|
+
}],
|
|
3266
|
+
};
|
|
3267
|
+
nodes.push(node);
|
|
3268
|
+
nodeById.set(nodeId, node);
|
|
3269
|
+
stateNodeIds.set(key, nodeId);
|
|
3270
|
+
return { nodeId, created: true };
|
|
3271
|
+
}
|
|
3272
|
+
normalizeState(state) {
|
|
3273
|
+
if (!state || typeof state !== 'object' || Array.isArray(state))
|
|
3274
|
+
return null;
|
|
3275
|
+
if ('values' in state || 'schema' in state || 'derived' in state)
|
|
3276
|
+
return state;
|
|
3277
|
+
return null;
|
|
3278
|
+
}
|
|
3095
3279
|
ensurePort(nodeById, nodeId, portId, kind, colWidth) {
|
|
3096
3280
|
const n = nodeById.get(nodeId);
|
|
3097
3281
|
if (!n)
|
|
@@ -3171,6 +3355,21 @@ class ConnectionGraphComponent {
|
|
|
3171
3355
|
initialSnapshot = '[]';
|
|
3172
3356
|
placeholderSample = 'payload | payload.id | ${payload.id}';
|
|
3173
3357
|
connectedPorts = new Map();
|
|
3358
|
+
isWidgetSource(from) {
|
|
3359
|
+
return 'widget' in from;
|
|
3360
|
+
}
|
|
3361
|
+
isWidgetTarget(to) {
|
|
3362
|
+
return 'widget' in to;
|
|
3363
|
+
}
|
|
3364
|
+
fromLabel(c) {
|
|
3365
|
+
return this.isWidgetSource(c.from) ? `${c.from.widget}.${c.from.output}` : `state:${c.from.state}`;
|
|
3366
|
+
}
|
|
3367
|
+
toLabel(c) {
|
|
3368
|
+
return this.isWidgetTarget(c.to) ? `${c.to.widget}.${c.to.input}` : `state:${c.to.state}`;
|
|
3369
|
+
}
|
|
3370
|
+
bindingOrderLabel(c) {
|
|
3371
|
+
return this.isWidgetTarget(c.to) && c.to.bindingOrder?.length ? c.to.bindingOrder.join(', ') : '';
|
|
3372
|
+
}
|
|
3174
3373
|
isPortConnected(nodeId, portId) {
|
|
3175
3374
|
const set = this.connectedPorts.get(nodeId);
|
|
3176
3375
|
return !!set && set.has(portId);
|
|
@@ -3244,12 +3443,12 @@ class ConnectionGraphComponent {
|
|
|
3244
3443
|
}
|
|
3245
3444
|
// External focus by connection (from Builder or other callers)
|
|
3246
3445
|
focusConnection(conn) {
|
|
3247
|
-
const fromNodeId = conn.from.widget
|
|
3248
|
-
const fromPortId = `out:${conn.from.output}
|
|
3249
|
-
let toNodeId = conn.to.widget
|
|
3250
|
-
let toPortId = `in:${conn.to.input}
|
|
3251
|
-
const deep = this.parseTabsPath(conn.to.input);
|
|
3252
|
-
if (deep) {
|
|
3446
|
+
const fromNodeId = this.isWidgetSource(conn.from) ? conn.from.widget : `state:output:${encodeURIComponent(conn.from.state)}`;
|
|
3447
|
+
const fromPortId = this.isWidgetSource(conn.from) ? `out:${conn.from.output}` : 'out:state';
|
|
3448
|
+
let toNodeId = this.isWidgetTarget(conn.to) ? conn.to.widget : `state:input:${encodeURIComponent(conn.to.state)}`;
|
|
3449
|
+
let toPortId = this.isWidgetTarget(conn.to) ? `in:${conn.to.input}` : 'in:state';
|
|
3450
|
+
const deep = this.isWidgetTarget(conn.to) ? this.parseTabsPath(conn.to.input) : null;
|
|
3451
|
+
if (deep && this.isWidgetTarget(conn.to)) {
|
|
3253
3452
|
const childId = `${conn.to.widget}#${deep.kind}[${deep.index}]/${deep.widgetIndex}`;
|
|
3254
3453
|
if (this.nodes().some(n => n.id === childId)) {
|
|
3255
3454
|
toNodeId = childId;
|
|
@@ -3619,10 +3818,10 @@ class ConnectionGraphComponent {
|
|
|
3619
3818
|
<button mat-icon-button (click)="showDetails=false" aria-label="Fechar"><mat-icon>close</mat-icon></button>
|
|
3620
3819
|
</div>
|
|
3621
3820
|
<div class="details-body" *ngIf="currentConn() as c">
|
|
3622
|
-
<div><b>De:</b> {{ c
|
|
3623
|
-
<div><b>Para:</b> {{ c
|
|
3821
|
+
<div><b>De:</b> {{ fromLabel(c) }}</div>
|
|
3822
|
+
<div><b>Para:</b> {{ toLabel(c) }}</div>
|
|
3624
3823
|
<div><b>map:</b> {{ c.map || 'payload' }}</div>
|
|
3625
|
-
<div *ngIf="c
|
|
3824
|
+
<div *ngIf="bindingOrderLabel(c)"><b>bindingOrder:</b> {{ bindingOrderLabel(c) }}</div>
|
|
3626
3825
|
<div class="details-actions">
|
|
3627
3826
|
<button mat-button color="primary" (click)="openBuilder()"><mat-icon>tune</mat-icon> Editar no Builder</button>
|
|
3628
3827
|
<button mat-button color="warn" (click)="removeEdge(selectedEdgeIndex())"><mat-icon>delete</mat-icon> Remover</button>
|
|
@@ -3755,10 +3954,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3755
3954
|
<button mat-icon-button (click)="showDetails=false" aria-label="Fechar"><mat-icon>close</mat-icon></button>
|
|
3756
3955
|
</div>
|
|
3757
3956
|
<div class="details-body" *ngIf="currentConn() as c">
|
|
3758
|
-
<div><b>De:</b> {{ c
|
|
3759
|
-
<div><b>Para:</b> {{ c
|
|
3957
|
+
<div><b>De:</b> {{ fromLabel(c) }}</div>
|
|
3958
|
+
<div><b>Para:</b> {{ toLabel(c) }}</div>
|
|
3760
3959
|
<div><b>map:</b> {{ c.map || 'payload' }}</div>
|
|
3761
|
-
<div *ngIf="c
|
|
3960
|
+
<div *ngIf="bindingOrderLabel(c)"><b>bindingOrder:</b> {{ bindingOrderLabel(c) }}</div>
|
|
3762
3961
|
<div class="details-actions">
|
|
3763
3962
|
<button mat-button color="primary" (click)="openBuilder()"><mat-icon>tune</mat-icon> Editar no Builder</button>
|
|
3764
3963
|
<button mat-button color="warn" (click)="removeEdge(selectedEdgeIndex())"><mat-icon>delete</mat-icon> Remover</button>
|
|
@@ -3945,7 +4144,7 @@ class ConnectionEditorComponent {
|
|
|
3945
4144
|
return input;
|
|
3946
4145
|
}
|
|
3947
4146
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ConnectionEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3948
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ConnectionEditorComponent, isStandalone: true, selector: "praxis-connection-editor", inputs: { page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: false, transformFunction: null }, widgets: { classPropertyName: "widgets", publicName: "widgets", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageChange: "pageChange" }, ngImport: i0, template: "<div class=\"conn-editor\">\n <mat-tab-group class=\"conn-tabs\">\n <mat-tab label=\"Construtor\">\n <praxis-connection-builder\n [page]=\"pageState() || undefined\"\n [widgets]=\"widgets() || undefined\"\n (pageChange)=\"onPageChange($event)\"\n ></praxis-connection-builder>\n </mat-tab>\n <mat-tab label=\"Diagrama\">\n <praxis-connection-graph\n [page]=\"pageState() || undefined\"\n [widgets]=\"widgets() || undefined\"\n (pageChange)=\"onPageChange($event)\"\n ></praxis-connection-graph>\n </mat-tab>\n <mat-tab label=\"JSON\">\n <div class=\"conn-json\">\n <mat-form-field class=\"json-field\" appearance=\"outline\">\n <mat-label>Pagina (JSON)</mat-label>\n <textarea matInput rows=\"16\" [formControl]=\"jsonControl\"></textarea>\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n class=\"help-icon-button\"\n matTooltip=\"Use este JSON para copiar/colar a p\u00E1gina completa.\"\n >\n <mat-icon>help_outline</mat-icon>\n </button>\n </mat-form-field>\n @if (jsonError()) {\n <div class=\"json-error\" role=\"alert\">{{ jsonError() }}</div>\n }\n <div class=\"json-actions\">\n <button mat-stroked-button type=\"button\" (click)=\"resetJson()\">Recarregar</button>\n </div>\n </div>\n </mat-tab>\n </mat-tab-group>\n</div>\n", styles: [".conn-editor{display:flex;flex-direction:column;height:100%;min-height:480px;color:var(--md-sys-color-on-surface)}.conn-tabs{flex:1}.conn-json{display:grid;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.json-field{width:100%}.json-error{color:var(--md-sys-color-error);font-size:13px}.json-actions{display:flex;justify-content:flex-end}.conn-editor .mat-mdc-form-field{width:100%;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.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;vertical-align:middle}.help-icon-button mat-icon{font-size:18px;line-height:18px;width:18px;height:18px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i9
|
|
4147
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ConnectionEditorComponent, isStandalone: true, selector: "praxis-connection-editor", inputs: { page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: false, transformFunction: null }, widgets: { classPropertyName: "widgets", publicName: "widgets", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageChange: "pageChange" }, ngImport: i0, template: "<div class=\"conn-editor\">\n <mat-tab-group class=\"conn-tabs\">\n <mat-tab label=\"Construtor\">\n <praxis-connection-builder\n [page]=\"pageState() || undefined\"\n [widgets]=\"widgets() || undefined\"\n (pageChange)=\"onPageChange($event)\"\n ></praxis-connection-builder>\n </mat-tab>\n <mat-tab label=\"Diagrama\">\n <praxis-connection-graph\n [page]=\"pageState() || undefined\"\n [widgets]=\"widgets() || undefined\"\n (pageChange)=\"onPageChange($event)\"\n ></praxis-connection-graph>\n </mat-tab>\n <mat-tab label=\"JSON\">\n <div class=\"conn-json\">\n <mat-form-field class=\"json-field\" appearance=\"outline\">\n <mat-label>Pagina (JSON)</mat-label>\n <textarea matInput rows=\"16\" [formControl]=\"jsonControl\"></textarea>\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n class=\"help-icon-button\"\n matTooltip=\"Use este JSON para copiar/colar a p\u00E1gina completa.\"\n >\n <mat-icon>help_outline</mat-icon>\n </button>\n </mat-form-field>\n @if (jsonError()) {\n <div class=\"json-error\" role=\"alert\">{{ jsonError() }}</div>\n }\n <div class=\"json-actions\">\n <button mat-stroked-button type=\"button\" (click)=\"resetJson()\">Recarregar</button>\n </div>\n </div>\n </mat-tab>\n </mat-tab-group>\n</div>\n", styles: [".conn-editor{display:flex;flex-direction:column;height:100%;min-height:480px;color:var(--md-sys-color-on-surface)}.conn-tabs{flex:1}.conn-json{display:grid;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.json-field{width:100%}.json-error{color:var(--md-sys-color-error);font-size:13px}.json-actions{display:flex;justify-content:flex-end}.conn-editor .mat-mdc-form-field{width:100%;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.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;vertical-align:middle}.help-icon-button mat-icon{font-size:18px;line-height:18px;width:18px;height:18px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i9.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i9.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3$2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.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: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ConnectionBuilderComponent, selector: "praxis-connection-builder", inputs: ["page", "widgets"], outputs: ["pageChange"] }, { kind: "component", type: ConnectionGraphComponent, selector: "praxis-connection-graph", inputs: ["page", "widgets"], outputs: ["pageChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3949
4148
|
}
|
|
3950
4149
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ConnectionEditorComponent, decorators: [{
|
|
3951
4150
|
type: Component,
|
|
@@ -4052,8 +4251,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
4052
4251
|
args: [MAT_DIALOG_DATA]
|
|
4053
4252
|
}] }, { type: i1.MatDialogRef }] });
|
|
4054
4253
|
|
|
4254
|
+
const DERIVED_OPERATORS = ['merge-objects', 'pick', 'omit'];
|
|
4255
|
+
const MERGE_STRATEGIES = ['replace', 'merge', 'append', 'remove-keys'];
|
|
4055
4256
|
class PageConfigEditorComponent {
|
|
4056
|
-
contextExample = 'Ex.: {
|
|
4257
|
+
contextExample = 'Ex.: {"tenantId":"acme","locale":"pt-BR"}';
|
|
4258
|
+
stateValuesExample = 'Ex.: {"query":{"filter":{"year":2026}},"selection":{"department":"Finance"}}';
|
|
4259
|
+
derivedOperators = DERIVED_OPERATORS;
|
|
4260
|
+
mergeStrategies = MERGE_STRATEGIES;
|
|
4057
4261
|
page = input(null, ...(ngDevMode ? [{ debugName: "page" }] : []));
|
|
4058
4262
|
identity = input(null, ...(ngDevMode ? [{ debugName: "identity" }] : []));
|
|
4059
4263
|
gridsterOptions = input(null, ...(ngDevMode ? [{ debugName: "gridsterOptions" }] : []));
|
|
@@ -4064,8 +4268,13 @@ class PageConfigEditorComponent {
|
|
|
4064
4268
|
gap: new FormControl(null),
|
|
4065
4269
|
});
|
|
4066
4270
|
contextControl = new FormControl('', { nonNullable: true });
|
|
4271
|
+
stateValuesControl = new FormControl('', { nonNullable: true });
|
|
4272
|
+
schemaNodes = new FormArray([]);
|
|
4273
|
+
derivedNodes = new FormArray([]);
|
|
4067
4274
|
contextError = signal(null, ...(ngDevMode ? [{ debugName: "contextError" }] : []));
|
|
4275
|
+
stateError = signal(null, ...(ngDevMode ? [{ debugName: "stateError" }] : []));
|
|
4068
4276
|
lastContext = '';
|
|
4277
|
+
lastStateValues = '';
|
|
4069
4278
|
constructor() {
|
|
4070
4279
|
effect(() => {
|
|
4071
4280
|
const next = this.parsePage(this.page());
|
|
@@ -4081,23 +4290,51 @@ class PageConfigEditorComponent {
|
|
|
4081
4290
|
this.contextControl.setValue(context, { emitEvent: false });
|
|
4082
4291
|
this.contextError.set(null);
|
|
4083
4292
|
}
|
|
4084
|
-
|
|
4293
|
+
this.hydrateStateEditor(next?.state);
|
|
4294
|
+
});
|
|
4085
4295
|
this.contextControl.valueChanges
|
|
4086
4296
|
.pipe(takeUntilDestroyed())
|
|
4087
4297
|
.subscribe(() => this.contextError.set(null));
|
|
4298
|
+
this.stateValuesControl.valueChanges
|
|
4299
|
+
.pipe(takeUntilDestroyed())
|
|
4300
|
+
.subscribe(() => this.stateError.set(null));
|
|
4301
|
+
this.schemaNodes.valueChanges
|
|
4302
|
+
.pipe(takeUntilDestroyed())
|
|
4303
|
+
.subscribe(() => this.stateError.set(null));
|
|
4304
|
+
this.derivedNodes.valueChanges
|
|
4305
|
+
.pipe(takeUntilDestroyed())
|
|
4306
|
+
.subscribe(() => this.stateError.set(null));
|
|
4307
|
+
}
|
|
4308
|
+
addSchemaNode(seed) {
|
|
4309
|
+
this.schemaNodes.push(this.createSchemaNodeGroup(seed));
|
|
4310
|
+
}
|
|
4311
|
+
removeSchemaNode(index) {
|
|
4312
|
+
this.schemaNodes.removeAt(index);
|
|
4313
|
+
}
|
|
4314
|
+
addDerivedNode(seed) {
|
|
4315
|
+
this.derivedNodes.push(this.createDerivedNodeGroup(seed));
|
|
4316
|
+
}
|
|
4317
|
+
removeDerivedNode(index) {
|
|
4318
|
+
this.derivedNodes.removeAt(index);
|
|
4088
4319
|
}
|
|
4089
4320
|
apply() {
|
|
4090
4321
|
const next = this.parsePage(this.page()) || { widgets: [] };
|
|
4091
4322
|
const options = this.buildOptions(this.form.value);
|
|
4092
4323
|
const context = this.parseContext(this.contextControl.value);
|
|
4324
|
+
const state = this.buildState();
|
|
4093
4325
|
if (context === null) {
|
|
4094
4326
|
this.contextError.set('JSON invalido');
|
|
4095
4327
|
return;
|
|
4096
4328
|
}
|
|
4329
|
+
if (state === null) {
|
|
4330
|
+
this.stateError.set('JSON invalido');
|
|
4331
|
+
return;
|
|
4332
|
+
}
|
|
4097
4333
|
const updated = {
|
|
4098
4334
|
...next,
|
|
4099
4335
|
options,
|
|
4100
4336
|
context: context || undefined,
|
|
4337
|
+
state: state || undefined,
|
|
4101
4338
|
};
|
|
4102
4339
|
this.pageChange.emit(updated);
|
|
4103
4340
|
}
|
|
@@ -4113,6 +4350,197 @@ class PageConfigEditorComponent {
|
|
|
4113
4350
|
this.lastContext = context;
|
|
4114
4351
|
this.contextControl.setValue(context, { emitEvent: false });
|
|
4115
4352
|
this.contextError.set(null);
|
|
4353
|
+
this.hydrateStateEditor(next?.state);
|
|
4354
|
+
}
|
|
4355
|
+
hydrateStateEditor(state) {
|
|
4356
|
+
const normalized = this.normalizeState(state);
|
|
4357
|
+
const nextValues = normalized.values ? JSON.stringify(normalized.values, null, 2) : '';
|
|
4358
|
+
if (nextValues !== this.lastStateValues) {
|
|
4359
|
+
this.lastStateValues = nextValues;
|
|
4360
|
+
this.stateValuesControl.setValue(nextValues, { emitEvent: false });
|
|
4361
|
+
this.stateError.set(null);
|
|
4362
|
+
}
|
|
4363
|
+
this.replaceSchemaNodes(normalized.schema);
|
|
4364
|
+
this.replaceDerivedNodes(normalized.derived);
|
|
4365
|
+
}
|
|
4366
|
+
replaceSchemaNodes(schema) {
|
|
4367
|
+
this.schemaNodes.clear({ emitEvent: false });
|
|
4368
|
+
for (const [path, config] of Object.entries(schema || {})) {
|
|
4369
|
+
this.schemaNodes.push(this.createSchemaNodeGroup({ path, config }), { emitEvent: false });
|
|
4370
|
+
}
|
|
4371
|
+
}
|
|
4372
|
+
replaceDerivedNodes(derived) {
|
|
4373
|
+
this.derivedNodes.clear({ emitEvent: false });
|
|
4374
|
+
for (const [path, config] of Object.entries(derived || {})) {
|
|
4375
|
+
this.derivedNodes.push(this.createDerivedNodeGroup({ path, config }), { emitEvent: false });
|
|
4376
|
+
}
|
|
4377
|
+
}
|
|
4378
|
+
createSchemaNodeGroup(seed) {
|
|
4379
|
+
const config = seed?.config || {};
|
|
4380
|
+
return new FormGroup({
|
|
4381
|
+
path: new FormControl(seed?.path || '', { nonNullable: true, validators: [Validators.required] }),
|
|
4382
|
+
type: new FormControl(config.type || '', { nonNullable: true }),
|
|
4383
|
+
initial: new FormControl(config.initial === undefined ? '' : JSON.stringify(config.initial, null, 2), { nonNullable: true }),
|
|
4384
|
+
persist: new FormControl(typeof config.persist === 'boolean' ? String(config.persist) : '', { nonNullable: true }),
|
|
4385
|
+
mergeStrategy: new FormControl(config.mergeStrategy || '', { nonNullable: true }),
|
|
4386
|
+
description: new FormControl(config.description || '', { nonNullable: true }),
|
|
4387
|
+
});
|
|
4388
|
+
}
|
|
4389
|
+
createDerivedNodeGroup(seed) {
|
|
4390
|
+
const config = seed?.config;
|
|
4391
|
+
const compute = config?.compute;
|
|
4392
|
+
const normalizedComputeKind = compute?.kind === 'operator' && compute.operator === 'template'
|
|
4393
|
+
? 'template'
|
|
4394
|
+
: compute?.kind || 'operator';
|
|
4395
|
+
const operatorOptions = compute?.kind === 'operator'
|
|
4396
|
+
? compute.options || {}
|
|
4397
|
+
: {};
|
|
4398
|
+
const sourcePath = typeof operatorOptions['source'] === 'string'
|
|
4399
|
+
? operatorOptions['source']
|
|
4400
|
+
: '';
|
|
4401
|
+
const keysCsv = Array.isArray(operatorOptions['keys'])
|
|
4402
|
+
? operatorOptions['keys'].filter((item) => typeof item === 'string').join(', ')
|
|
4403
|
+
: '';
|
|
4404
|
+
return new FormGroup({
|
|
4405
|
+
path: new FormControl(seed?.path || '', { nonNullable: true, validators: [Validators.required] }),
|
|
4406
|
+
dependsOn: new FormControl((config?.dependsOn || []).join(', '), { nonNullable: true }),
|
|
4407
|
+
computeKind: new FormControl(normalizedComputeKind, { nonNullable: true }),
|
|
4408
|
+
operator: new FormControl(compute?.kind === 'operator' ? compute.operator || 'merge-objects' : 'merge-objects', {
|
|
4409
|
+
nonNullable: true,
|
|
4410
|
+
}),
|
|
4411
|
+
sourcePath: new FormControl(sourcePath, { nonNullable: true }),
|
|
4412
|
+
keysCsv: new FormControl(keysCsv, { nonNullable: true }),
|
|
4413
|
+
expression: new FormControl(compute?.kind === 'expr' ? compute.expression || '' : '', { nonNullable: true }),
|
|
4414
|
+
transformerId: new FormControl(compute?.kind === 'transformer' ? compute.transformerId || '' : '', { nonNullable: true }),
|
|
4415
|
+
options: new FormControl(compute?.kind === 'transformer'
|
|
4416
|
+
? JSON.stringify(compute.options || {}, null, 2)
|
|
4417
|
+
: '', { nonNullable: true }),
|
|
4418
|
+
template: new FormControl(compute?.kind === 'template'
|
|
4419
|
+
? JSON.stringify(compute.value, null, 2)
|
|
4420
|
+
: compute?.kind === 'operator' && compute.operator === 'template'
|
|
4421
|
+
? JSON.stringify(compute.options?.['value'], null, 2)
|
|
4422
|
+
: '', { nonNullable: true }),
|
|
4423
|
+
description: new FormControl(config?.description || '', { nonNullable: true }),
|
|
4424
|
+
});
|
|
4425
|
+
}
|
|
4426
|
+
buildState() {
|
|
4427
|
+
const values = this.parseJson(this.stateValuesControl.value, {});
|
|
4428
|
+
if (values === null)
|
|
4429
|
+
return null;
|
|
4430
|
+
const schema = this.buildSchema();
|
|
4431
|
+
if (schema === null)
|
|
4432
|
+
return null;
|
|
4433
|
+
const derived = this.buildDerived();
|
|
4434
|
+
if (derived === null)
|
|
4435
|
+
return null;
|
|
4436
|
+
if (!Object.keys(values).length && !Object.keys(schema).length && !Object.keys(derived).length) {
|
|
4437
|
+
return undefined;
|
|
4438
|
+
}
|
|
4439
|
+
const state = {};
|
|
4440
|
+
if (Object.keys(values).length)
|
|
4441
|
+
state.values = values;
|
|
4442
|
+
if (Object.keys(schema).length)
|
|
4443
|
+
state.schema = schema;
|
|
4444
|
+
if (Object.keys(derived).length)
|
|
4445
|
+
state.derived = derived;
|
|
4446
|
+
return state;
|
|
4447
|
+
}
|
|
4448
|
+
buildSchema() {
|
|
4449
|
+
const schema = {};
|
|
4450
|
+
for (const group of this.schemaNodes.controls) {
|
|
4451
|
+
const path = group.controls.path.value.trim();
|
|
4452
|
+
if (!path)
|
|
4453
|
+
continue;
|
|
4454
|
+
const initial = this.parseJson(group.controls.initial.value, undefined);
|
|
4455
|
+
if (initial === null)
|
|
4456
|
+
return null;
|
|
4457
|
+
const node = {};
|
|
4458
|
+
const type = group.controls.type.value.trim();
|
|
4459
|
+
const persist = group.controls.persist.value.trim();
|
|
4460
|
+
const mergeStrategy = group.controls.mergeStrategy.value.trim();
|
|
4461
|
+
const description = group.controls.description.value.trim();
|
|
4462
|
+
if (type)
|
|
4463
|
+
node.type = type;
|
|
4464
|
+
if (initial !== undefined)
|
|
4465
|
+
node.initial = initial;
|
|
4466
|
+
if (persist === 'true')
|
|
4467
|
+
node.persist = true;
|
|
4468
|
+
if (persist === 'false')
|
|
4469
|
+
node.persist = false;
|
|
4470
|
+
if (mergeStrategy)
|
|
4471
|
+
node.mergeStrategy = mergeStrategy;
|
|
4472
|
+
if (description)
|
|
4473
|
+
node.description = description;
|
|
4474
|
+
schema[path] = node;
|
|
4475
|
+
}
|
|
4476
|
+
return schema;
|
|
4477
|
+
}
|
|
4478
|
+
buildDerived() {
|
|
4479
|
+
const derived = {};
|
|
4480
|
+
for (const group of this.derivedNodes.controls) {
|
|
4481
|
+
const path = group.controls.path.value.trim();
|
|
4482
|
+
if (!path)
|
|
4483
|
+
continue;
|
|
4484
|
+
const dependsOn = group.controls.dependsOn.value
|
|
4485
|
+
.split(',')
|
|
4486
|
+
.map((item) => item.trim())
|
|
4487
|
+
.filter(Boolean);
|
|
4488
|
+
const computeKind = group.controls.computeKind.value;
|
|
4489
|
+
const description = group.controls.description.value.trim();
|
|
4490
|
+
let compute;
|
|
4491
|
+
if (computeKind === 'template') {
|
|
4492
|
+
const value = this.parseJson(group.controls.template.value, undefined);
|
|
4493
|
+
if (value === null)
|
|
4494
|
+
return null;
|
|
4495
|
+
compute = { kind: 'template', value };
|
|
4496
|
+
}
|
|
4497
|
+
else if (computeKind === 'expr') {
|
|
4498
|
+
compute = { kind: 'expr', expression: group.controls.expression.value.trim() };
|
|
4499
|
+
}
|
|
4500
|
+
else if (computeKind === 'transformer') {
|
|
4501
|
+
const options = this.parseJson(group.controls.options.value, {});
|
|
4502
|
+
if (options === null)
|
|
4503
|
+
return null;
|
|
4504
|
+
compute = {
|
|
4505
|
+
kind: 'transformer',
|
|
4506
|
+
transformerId: group.controls.transformerId.value.trim(),
|
|
4507
|
+
options,
|
|
4508
|
+
};
|
|
4509
|
+
}
|
|
4510
|
+
else {
|
|
4511
|
+
const operator = group.controls.operator.value.trim() || 'merge-objects';
|
|
4512
|
+
const options = this.buildOperatorOptions(group);
|
|
4513
|
+
if (options === null)
|
|
4514
|
+
return null;
|
|
4515
|
+
compute = {
|
|
4516
|
+
kind: 'operator',
|
|
4517
|
+
operator,
|
|
4518
|
+
options,
|
|
4519
|
+
};
|
|
4520
|
+
}
|
|
4521
|
+
derived[path] = {
|
|
4522
|
+
dependsOn,
|
|
4523
|
+
compute,
|
|
4524
|
+
...(description ? { description } : {}),
|
|
4525
|
+
};
|
|
4526
|
+
}
|
|
4527
|
+
return derived;
|
|
4528
|
+
}
|
|
4529
|
+
normalizeState(state) {
|
|
4530
|
+
if (!state)
|
|
4531
|
+
return { values: {} };
|
|
4532
|
+
const isStructured = typeof state === 'object'
|
|
4533
|
+
&& !Array.isArray(state)
|
|
4534
|
+
&& ('values' in state || 'schema' in state || 'derived' in state);
|
|
4535
|
+
if (isStructured) {
|
|
4536
|
+
const structured = state;
|
|
4537
|
+
return {
|
|
4538
|
+
values: this.clone(structured.values) || {},
|
|
4539
|
+
schema: this.clone(structured.schema),
|
|
4540
|
+
derived: this.clone(structured.derived),
|
|
4541
|
+
};
|
|
4542
|
+
}
|
|
4543
|
+
return { values: this.clone(state) || {} };
|
|
4116
4544
|
}
|
|
4117
4545
|
parsePage(input) {
|
|
4118
4546
|
if (!input)
|
|
@@ -4137,6 +4565,33 @@ class PageConfigEditorComponent {
|
|
|
4137
4565
|
return null;
|
|
4138
4566
|
}
|
|
4139
4567
|
}
|
|
4568
|
+
parseJson(raw, emptyValue) {
|
|
4569
|
+
if (!raw.trim())
|
|
4570
|
+
return emptyValue;
|
|
4571
|
+
try {
|
|
4572
|
+
return JSON.parse(raw);
|
|
4573
|
+
}
|
|
4574
|
+
catch {
|
|
4575
|
+
return null;
|
|
4576
|
+
}
|
|
4577
|
+
}
|
|
4578
|
+
buildOperatorOptions(group) {
|
|
4579
|
+
const operator = group.controls.operator.value.trim();
|
|
4580
|
+
if (operator === 'pick' || operator === 'omit') {
|
|
4581
|
+
const source = group.controls.sourcePath.value.trim();
|
|
4582
|
+
const keys = group.controls.keysCsv.value
|
|
4583
|
+
.split(',')
|
|
4584
|
+
.map((item) => item.trim())
|
|
4585
|
+
.filter(Boolean);
|
|
4586
|
+
const options = {};
|
|
4587
|
+
if (source)
|
|
4588
|
+
options['source'] = source;
|
|
4589
|
+
if (keys.length)
|
|
4590
|
+
options['keys'] = keys;
|
|
4591
|
+
return options;
|
|
4592
|
+
}
|
|
4593
|
+
return {};
|
|
4594
|
+
}
|
|
4140
4595
|
buildOptions(values) {
|
|
4141
4596
|
const options = {};
|
|
4142
4597
|
if (values.cols != null)
|
|
@@ -4147,6 +4602,11 @@ class PageConfigEditorComponent {
|
|
|
4147
4602
|
options.gap = values.gap;
|
|
4148
4603
|
return options;
|
|
4149
4604
|
}
|
|
4605
|
+
clone(value) {
|
|
4606
|
+
if (value == null || typeof value !== 'object')
|
|
4607
|
+
return value;
|
|
4608
|
+
return JSON.parse(JSON.stringify(value));
|
|
4609
|
+
}
|
|
4150
4610
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PageConfigEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4151
4611
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PageConfigEditorComponent, isStandalone: true, selector: "praxis-page-config-editor", inputs: { page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: false, transformFunction: null }, identity: { classPropertyName: "identity", publicName: "identity", isSignal: true, isRequired: false, transformFunction: null }, gridsterOptions: { classPropertyName: "gridsterOptions", publicName: "gridsterOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageChange: "pageChange" }, ngImport: i0, template: `
|
|
4152
4612
|
<div class="page-config">
|
|
@@ -4204,15 +4664,202 @@ class PageConfigEditorComponent {
|
|
|
4204
4664
|
<div class="page-error" role="alert">{{ contextError() }}</div>
|
|
4205
4665
|
}
|
|
4206
4666
|
|
|
4667
|
+
<div class="page-section">Estado da Página</div>
|
|
4668
|
+
<div class="state-grid">
|
|
4669
|
+
<mat-form-field appearance="outline" class="state-values">
|
|
4670
|
+
<mat-label>Valores primários (JSON)</mat-label>
|
|
4671
|
+
<textarea matInput rows="10" [formControl]="stateValuesControl"></textarea>
|
|
4672
|
+
<button
|
|
4673
|
+
mat-icon-button
|
|
4674
|
+
matSuffix
|
|
4675
|
+
class="help-icon-button"
|
|
4676
|
+
type="button"
|
|
4677
|
+
[matTooltip]="stateValuesExample"
|
|
4678
|
+
matTooltipPosition="above"
|
|
4679
|
+
>
|
|
4680
|
+
<mat-icon>help_outline</mat-icon>
|
|
4681
|
+
</button>
|
|
4682
|
+
</mat-form-field>
|
|
4683
|
+
|
|
4684
|
+
<div class="state-group">
|
|
4685
|
+
<div class="state-group-head">
|
|
4686
|
+
<div class="state-group-title">Schema</div>
|
|
4687
|
+
<button mat-stroked-button type="button" (click)="addSchemaNode()">
|
|
4688
|
+
<mat-icon>add</mat-icon>
|
|
4689
|
+
Adicionar nó
|
|
4690
|
+
</button>
|
|
4691
|
+
</div>
|
|
4692
|
+
<div class="state-group-help">Define caminhos primários, valor inicial e estratégia de merge.</div>
|
|
4693
|
+
@if (!schemaNodes.length) {
|
|
4694
|
+
<div class="state-empty">Nenhum nó de schema configurado.</div>
|
|
4695
|
+
}
|
|
4696
|
+
<div class="state-list">
|
|
4697
|
+
@for (group of schemaNodes.controls; track $index) {
|
|
4698
|
+
<div class="state-card" [formGroup]="group">
|
|
4699
|
+
<div class="state-card-head">
|
|
4700
|
+
<div class="state-card-title">Nó {{ $index + 1 }}</div>
|
|
4701
|
+
<button mat-icon-button type="button" (click)="removeSchemaNode($index)" aria-label="Remover nó de schema">
|
|
4702
|
+
<mat-icon>delete</mat-icon>
|
|
4703
|
+
</button>
|
|
4704
|
+
</div>
|
|
4705
|
+
<div class="state-card-grid">
|
|
4706
|
+
<mat-form-field appearance="outline">
|
|
4707
|
+
<mat-label>Path</mat-label>
|
|
4708
|
+
<input matInput formControlName="path" />
|
|
4709
|
+
</mat-form-field>
|
|
4710
|
+
<mat-form-field appearance="outline">
|
|
4711
|
+
<mat-label>Tipo</mat-label>
|
|
4712
|
+
<input matInput formControlName="type" />
|
|
4713
|
+
</mat-form-field>
|
|
4714
|
+
<mat-form-field appearance="outline">
|
|
4715
|
+
<mat-label>Persistência</mat-label>
|
|
4716
|
+
<mat-select formControlName="persist">
|
|
4717
|
+
<mat-option value="">Padrão</mat-option>
|
|
4718
|
+
<mat-option value="true">Persistir</mat-option>
|
|
4719
|
+
<mat-option value="false">Não persistir</mat-option>
|
|
4720
|
+
</mat-select>
|
|
4721
|
+
</mat-form-field>
|
|
4722
|
+
<mat-form-field appearance="outline">
|
|
4723
|
+
<mat-label>Merge</mat-label>
|
|
4724
|
+
<mat-select formControlName="mergeStrategy">
|
|
4725
|
+
<mat-option value="">Padrão</mat-option>
|
|
4726
|
+
@for (strategy of mergeStrategies; track strategy) {
|
|
4727
|
+
<mat-option [value]="strategy">{{ strategy }}</mat-option>
|
|
4728
|
+
}
|
|
4729
|
+
</mat-select>
|
|
4730
|
+
</mat-form-field>
|
|
4731
|
+
<mat-form-field appearance="outline" class="span-2">
|
|
4732
|
+
<mat-label>Initial (JSON)</mat-label>
|
|
4733
|
+
<textarea matInput rows="4" formControlName="initial"></textarea>
|
|
4734
|
+
</mat-form-field>
|
|
4735
|
+
<mat-form-field appearance="outline" class="span-2">
|
|
4736
|
+
<mat-label>Descrição</mat-label>
|
|
4737
|
+
<input matInput formControlName="description" />
|
|
4738
|
+
</mat-form-field>
|
|
4739
|
+
</div>
|
|
4740
|
+
</div>
|
|
4741
|
+
}
|
|
4742
|
+
</div>
|
|
4743
|
+
</div>
|
|
4744
|
+
|
|
4745
|
+
<div class="state-group">
|
|
4746
|
+
<div class="state-group-head">
|
|
4747
|
+
<div class="state-group-title">Derived</div>
|
|
4748
|
+
<button mat-stroked-button type="button" (click)="addDerivedNode()">
|
|
4749
|
+
<mat-icon>add</mat-icon>
|
|
4750
|
+
Adicionar derivação
|
|
4751
|
+
</button>
|
|
4752
|
+
</div>
|
|
4753
|
+
<div class="state-group-help">Modela recomputações do runtime com dependsOn e operadores canônicos.</div>
|
|
4754
|
+
@if (!derivedNodes.length) {
|
|
4755
|
+
<div class="state-empty">Nenhum nó derivado configurado.</div>
|
|
4756
|
+
}
|
|
4757
|
+
<div class="state-list">
|
|
4758
|
+
@for (group of derivedNodes.controls; track $index) {
|
|
4759
|
+
<div class="state-card" [formGroup]="group">
|
|
4760
|
+
<div class="state-card-head">
|
|
4761
|
+
<div class="state-card-title">Derived {{ $index + 1 }}</div>
|
|
4762
|
+
<button mat-icon-button type="button" (click)="removeDerivedNode($index)" aria-label="Remover derivação">
|
|
4763
|
+
<mat-icon>delete</mat-icon>
|
|
4764
|
+
</button>
|
|
4765
|
+
</div>
|
|
4766
|
+
<div class="state-card-grid">
|
|
4767
|
+
<mat-form-field appearance="outline">
|
|
4768
|
+
<mat-label>Path</mat-label>
|
|
4769
|
+
<input matInput formControlName="path" />
|
|
4770
|
+
</mat-form-field>
|
|
4771
|
+
<mat-form-field appearance="outline">
|
|
4772
|
+
<mat-label>Compute</mat-label>
|
|
4773
|
+
<mat-select formControlName="computeKind">
|
|
4774
|
+
<mat-option value="operator">Operator</mat-option>
|
|
4775
|
+
<mat-option value="template">Template</mat-option>
|
|
4776
|
+
<mat-option value="expr">Expr</mat-option>
|
|
4777
|
+
<mat-option value="transformer">Transformer</mat-option>
|
|
4778
|
+
</mat-select>
|
|
4779
|
+
</mat-form-field>
|
|
4780
|
+
<mat-form-field appearance="outline" class="span-2">
|
|
4781
|
+
<mat-label>DependsOn (vírgula)</mat-label>
|
|
4782
|
+
<input matInput formControlName="dependsOn" />
|
|
4783
|
+
</mat-form-field>
|
|
4784
|
+
@if (group.controls.computeKind.value === 'operator') {
|
|
4785
|
+
<mat-form-field appearance="outline">
|
|
4786
|
+
<mat-label>Operator</mat-label>
|
|
4787
|
+
<mat-select formControlName="operator">
|
|
4788
|
+
@for (operator of derivedOperators; track operator) {
|
|
4789
|
+
<mat-option [value]="operator">{{ operator }}</mat-option>
|
|
4790
|
+
}
|
|
4791
|
+
</mat-select>
|
|
4792
|
+
</mat-form-field>
|
|
4793
|
+
@if (group.controls.operator.value === 'pick' || group.controls.operator.value === 'omit') {
|
|
4794
|
+
<mat-form-field appearance="outline">
|
|
4795
|
+
<mat-label>Source path</mat-label>
|
|
4796
|
+
<input matInput formControlName="sourcePath" />
|
|
4797
|
+
</mat-form-field>
|
|
4798
|
+
<mat-form-field appearance="outline" class="span-2">
|
|
4799
|
+
<mat-label>Keys (vírgula)</mat-label>
|
|
4800
|
+
<input matInput formControlName="keysCsv" />
|
|
4801
|
+
</mat-form-field>
|
|
4802
|
+
} @else {
|
|
4803
|
+
<div class="state-operator-hint span-3">
|
|
4804
|
+
<mat-icon>account_tree</mat-icon>
|
|
4805
|
+
<span>O operador {{ group.controls.operator.value }} usa os caminhos de dependsOn como fonte principal.</span>
|
|
4806
|
+
</div>
|
|
4807
|
+
}
|
|
4808
|
+
}
|
|
4809
|
+
@if (group.controls.computeKind.value === 'template') {
|
|
4810
|
+
<mat-form-field appearance="outline" class="span-3">
|
|
4811
|
+
<mat-label>Template (JSON)</mat-label>
|
|
4812
|
+
<textarea matInput rows="4" formControlName="template"></textarea>
|
|
4813
|
+
</mat-form-field>
|
|
4814
|
+
}
|
|
4815
|
+
@if (group.controls.computeKind.value === 'expr') {
|
|
4816
|
+
<mat-form-field appearance="outline" class="span-3">
|
|
4817
|
+
<mat-label>Expression</mat-label>
|
|
4818
|
+
<input matInput formControlName="expression" />
|
|
4819
|
+
</mat-form-field>
|
|
4820
|
+
}
|
|
4821
|
+
@if (group.controls.computeKind.value === 'transformer') {
|
|
4822
|
+
<mat-form-field appearance="outline">
|
|
4823
|
+
<mat-label>Transformer</mat-label>
|
|
4824
|
+
<input matInput formControlName="transformerId" />
|
|
4825
|
+
</mat-form-field>
|
|
4826
|
+
<mat-form-field appearance="outline" class="span-2">
|
|
4827
|
+
<mat-label>Options (JSON)</mat-label>
|
|
4828
|
+
<textarea matInput rows="4" formControlName="options"></textarea>
|
|
4829
|
+
</mat-form-field>
|
|
4830
|
+
}
|
|
4831
|
+
<mat-form-field appearance="outline" class="span-3">
|
|
4832
|
+
<mat-label>Descrição</mat-label>
|
|
4833
|
+
<input matInput formControlName="description" />
|
|
4834
|
+
</mat-form-field>
|
|
4835
|
+
</div>
|
|
4836
|
+
</div>
|
|
4837
|
+
}
|
|
4838
|
+
</div>
|
|
4839
|
+
</div>
|
|
4840
|
+
</div>
|
|
4841
|
+
@if (stateError()) {
|
|
4842
|
+
<div class="page-error" role="alert">{{ stateError() }}</div>
|
|
4843
|
+
}
|
|
4844
|
+
|
|
4207
4845
|
<div class="page-actions">
|
|
4208
4846
|
<button mat-flat-button color="primary" type="button" (click)="apply()">Aplicar</button>
|
|
4209
4847
|
</div>
|
|
4210
4848
|
</div>
|
|
4211
|
-
`, isInline: true, styles: [".page-config{display:grid;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.page-head{display:flex;align-items:center;gap:12px}.page-title{font-weight:600}.spacer{flex:1}.page-identity{display:grid;gap:4px;font-size:13px;color:var(--md-sys-color-on-surface-variant)}.page-section{font-weight:600;margin-top:8px}.grid-form{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.page-actions{display:flex;justify-content:flex-end}.page-error{color:var(--md-sys-color-error);font-size:13px}.page-config .mat-mdc-form-field{width:100%;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.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: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3$2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.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: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4849
|
+
`, isInline: true, styles: [".page-config{display:grid;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.page-head{display:flex;align-items:center;gap:12px}.page-title{font-weight:600}.spacer{flex:1}.page-identity{display:grid;gap:4px;font-size:13px;color:var(--md-sys-color-on-surface-variant)}.page-section{font-weight:600;margin-top:8px}.grid-form{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.state-grid{display:grid;gap:16px}.state-values,.state-group{padding:14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:14px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-surface-container-low) 92%,transparent),transparent),var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface))}.state-group{display:grid;gap:12px}.state-group-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.state-group-title{font-weight:600}.state-group-help{color:var(--md-sys-color-on-surface-variant);font-size:13px}.state-list{display:grid;gap:12px}.state-card{display:grid;gap:12px;padding:12px;border-radius:12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 80%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container) 84%,transparent)}.state-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.state-card-title{font-weight:600}.state-card-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.span-2{grid-column:span 2}.span-3{grid-column:1 / -1}.state-empty{color:var(--md-sys-color-on-surface-variant);font-size:13px}.state-operator-hint{display:flex;align-items:center;gap:8px;min-height:48px;padding:0 12px;border-radius:12px;color:var(--md-sys-color-on-surface-variant);background:color-mix(in srgb,var(--md-sys-color-secondary-container) 35%,transparent)}.state-operator-hint mat-icon{color:var(--md-sys-color-secondary)}.page-actions{display:flex;justify-content:flex-end}.page-error{color:var(--md-sys-color-error);font-size:13px}.page-config .mat-mdc-form-field{width:100%;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.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}@media(max-width:720px){.span-2,.span-3{grid-column:auto}.state-group-head{align-items:stretch;flex-direction:column}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3$2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.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: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.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: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4212
4850
|
}
|
|
4213
4851
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PageConfigEditorComponent, decorators: [{
|
|
4214
4852
|
type: Component,
|
|
4215
|
-
args: [{ selector: 'praxis-page-config-editor', standalone: true, imports: [
|
|
4853
|
+
args: [{ selector: 'praxis-page-config-editor', standalone: true, imports: [
|
|
4854
|
+
CommonModule,
|
|
4855
|
+
ReactiveFormsModule,
|
|
4856
|
+
MatButtonModule,
|
|
4857
|
+
MatFormFieldModule,
|
|
4858
|
+
MatInputModule,
|
|
4859
|
+
MatIconModule,
|
|
4860
|
+
MatSelectModule,
|
|
4861
|
+
MatTooltipModule,
|
|
4862
|
+
], template: `
|
|
4216
4863
|
<div class="page-config">
|
|
4217
4864
|
<div class="page-head">
|
|
4218
4865
|
<div class="page-title">Configurações da Página</div>
|
|
@@ -4268,11 +4915,189 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
4268
4915
|
<div class="page-error" role="alert">{{ contextError() }}</div>
|
|
4269
4916
|
}
|
|
4270
4917
|
|
|
4918
|
+
<div class="page-section">Estado da Página</div>
|
|
4919
|
+
<div class="state-grid">
|
|
4920
|
+
<mat-form-field appearance="outline" class="state-values">
|
|
4921
|
+
<mat-label>Valores primários (JSON)</mat-label>
|
|
4922
|
+
<textarea matInput rows="10" [formControl]="stateValuesControl"></textarea>
|
|
4923
|
+
<button
|
|
4924
|
+
mat-icon-button
|
|
4925
|
+
matSuffix
|
|
4926
|
+
class="help-icon-button"
|
|
4927
|
+
type="button"
|
|
4928
|
+
[matTooltip]="stateValuesExample"
|
|
4929
|
+
matTooltipPosition="above"
|
|
4930
|
+
>
|
|
4931
|
+
<mat-icon>help_outline</mat-icon>
|
|
4932
|
+
</button>
|
|
4933
|
+
</mat-form-field>
|
|
4934
|
+
|
|
4935
|
+
<div class="state-group">
|
|
4936
|
+
<div class="state-group-head">
|
|
4937
|
+
<div class="state-group-title">Schema</div>
|
|
4938
|
+
<button mat-stroked-button type="button" (click)="addSchemaNode()">
|
|
4939
|
+
<mat-icon>add</mat-icon>
|
|
4940
|
+
Adicionar nó
|
|
4941
|
+
</button>
|
|
4942
|
+
</div>
|
|
4943
|
+
<div class="state-group-help">Define caminhos primários, valor inicial e estratégia de merge.</div>
|
|
4944
|
+
@if (!schemaNodes.length) {
|
|
4945
|
+
<div class="state-empty">Nenhum nó de schema configurado.</div>
|
|
4946
|
+
}
|
|
4947
|
+
<div class="state-list">
|
|
4948
|
+
@for (group of schemaNodes.controls; track $index) {
|
|
4949
|
+
<div class="state-card" [formGroup]="group">
|
|
4950
|
+
<div class="state-card-head">
|
|
4951
|
+
<div class="state-card-title">Nó {{ $index + 1 }}</div>
|
|
4952
|
+
<button mat-icon-button type="button" (click)="removeSchemaNode($index)" aria-label="Remover nó de schema">
|
|
4953
|
+
<mat-icon>delete</mat-icon>
|
|
4954
|
+
</button>
|
|
4955
|
+
</div>
|
|
4956
|
+
<div class="state-card-grid">
|
|
4957
|
+
<mat-form-field appearance="outline">
|
|
4958
|
+
<mat-label>Path</mat-label>
|
|
4959
|
+
<input matInput formControlName="path" />
|
|
4960
|
+
</mat-form-field>
|
|
4961
|
+
<mat-form-field appearance="outline">
|
|
4962
|
+
<mat-label>Tipo</mat-label>
|
|
4963
|
+
<input matInput formControlName="type" />
|
|
4964
|
+
</mat-form-field>
|
|
4965
|
+
<mat-form-field appearance="outline">
|
|
4966
|
+
<mat-label>Persistência</mat-label>
|
|
4967
|
+
<mat-select formControlName="persist">
|
|
4968
|
+
<mat-option value="">Padrão</mat-option>
|
|
4969
|
+
<mat-option value="true">Persistir</mat-option>
|
|
4970
|
+
<mat-option value="false">Não persistir</mat-option>
|
|
4971
|
+
</mat-select>
|
|
4972
|
+
</mat-form-field>
|
|
4973
|
+
<mat-form-field appearance="outline">
|
|
4974
|
+
<mat-label>Merge</mat-label>
|
|
4975
|
+
<mat-select formControlName="mergeStrategy">
|
|
4976
|
+
<mat-option value="">Padrão</mat-option>
|
|
4977
|
+
@for (strategy of mergeStrategies; track strategy) {
|
|
4978
|
+
<mat-option [value]="strategy">{{ strategy }}</mat-option>
|
|
4979
|
+
}
|
|
4980
|
+
</mat-select>
|
|
4981
|
+
</mat-form-field>
|
|
4982
|
+
<mat-form-field appearance="outline" class="span-2">
|
|
4983
|
+
<mat-label>Initial (JSON)</mat-label>
|
|
4984
|
+
<textarea matInput rows="4" formControlName="initial"></textarea>
|
|
4985
|
+
</mat-form-field>
|
|
4986
|
+
<mat-form-field appearance="outline" class="span-2">
|
|
4987
|
+
<mat-label>Descrição</mat-label>
|
|
4988
|
+
<input matInput formControlName="description" />
|
|
4989
|
+
</mat-form-field>
|
|
4990
|
+
</div>
|
|
4991
|
+
</div>
|
|
4992
|
+
}
|
|
4993
|
+
</div>
|
|
4994
|
+
</div>
|
|
4995
|
+
|
|
4996
|
+
<div class="state-group">
|
|
4997
|
+
<div class="state-group-head">
|
|
4998
|
+
<div class="state-group-title">Derived</div>
|
|
4999
|
+
<button mat-stroked-button type="button" (click)="addDerivedNode()">
|
|
5000
|
+
<mat-icon>add</mat-icon>
|
|
5001
|
+
Adicionar derivação
|
|
5002
|
+
</button>
|
|
5003
|
+
</div>
|
|
5004
|
+
<div class="state-group-help">Modela recomputações do runtime com dependsOn e operadores canônicos.</div>
|
|
5005
|
+
@if (!derivedNodes.length) {
|
|
5006
|
+
<div class="state-empty">Nenhum nó derivado configurado.</div>
|
|
5007
|
+
}
|
|
5008
|
+
<div class="state-list">
|
|
5009
|
+
@for (group of derivedNodes.controls; track $index) {
|
|
5010
|
+
<div class="state-card" [formGroup]="group">
|
|
5011
|
+
<div class="state-card-head">
|
|
5012
|
+
<div class="state-card-title">Derived {{ $index + 1 }}</div>
|
|
5013
|
+
<button mat-icon-button type="button" (click)="removeDerivedNode($index)" aria-label="Remover derivação">
|
|
5014
|
+
<mat-icon>delete</mat-icon>
|
|
5015
|
+
</button>
|
|
5016
|
+
</div>
|
|
5017
|
+
<div class="state-card-grid">
|
|
5018
|
+
<mat-form-field appearance="outline">
|
|
5019
|
+
<mat-label>Path</mat-label>
|
|
5020
|
+
<input matInput formControlName="path" />
|
|
5021
|
+
</mat-form-field>
|
|
5022
|
+
<mat-form-field appearance="outline">
|
|
5023
|
+
<mat-label>Compute</mat-label>
|
|
5024
|
+
<mat-select formControlName="computeKind">
|
|
5025
|
+
<mat-option value="operator">Operator</mat-option>
|
|
5026
|
+
<mat-option value="template">Template</mat-option>
|
|
5027
|
+
<mat-option value="expr">Expr</mat-option>
|
|
5028
|
+
<mat-option value="transformer">Transformer</mat-option>
|
|
5029
|
+
</mat-select>
|
|
5030
|
+
</mat-form-field>
|
|
5031
|
+
<mat-form-field appearance="outline" class="span-2">
|
|
5032
|
+
<mat-label>DependsOn (vírgula)</mat-label>
|
|
5033
|
+
<input matInput formControlName="dependsOn" />
|
|
5034
|
+
</mat-form-field>
|
|
5035
|
+
@if (group.controls.computeKind.value === 'operator') {
|
|
5036
|
+
<mat-form-field appearance="outline">
|
|
5037
|
+
<mat-label>Operator</mat-label>
|
|
5038
|
+
<mat-select formControlName="operator">
|
|
5039
|
+
@for (operator of derivedOperators; track operator) {
|
|
5040
|
+
<mat-option [value]="operator">{{ operator }}</mat-option>
|
|
5041
|
+
}
|
|
5042
|
+
</mat-select>
|
|
5043
|
+
</mat-form-field>
|
|
5044
|
+
@if (group.controls.operator.value === 'pick' || group.controls.operator.value === 'omit') {
|
|
5045
|
+
<mat-form-field appearance="outline">
|
|
5046
|
+
<mat-label>Source path</mat-label>
|
|
5047
|
+
<input matInput formControlName="sourcePath" />
|
|
5048
|
+
</mat-form-field>
|
|
5049
|
+
<mat-form-field appearance="outline" class="span-2">
|
|
5050
|
+
<mat-label>Keys (vírgula)</mat-label>
|
|
5051
|
+
<input matInput formControlName="keysCsv" />
|
|
5052
|
+
</mat-form-field>
|
|
5053
|
+
} @else {
|
|
5054
|
+
<div class="state-operator-hint span-3">
|
|
5055
|
+
<mat-icon>account_tree</mat-icon>
|
|
5056
|
+
<span>O operador {{ group.controls.operator.value }} usa os caminhos de dependsOn como fonte principal.</span>
|
|
5057
|
+
</div>
|
|
5058
|
+
}
|
|
5059
|
+
}
|
|
5060
|
+
@if (group.controls.computeKind.value === 'template') {
|
|
5061
|
+
<mat-form-field appearance="outline" class="span-3">
|
|
5062
|
+
<mat-label>Template (JSON)</mat-label>
|
|
5063
|
+
<textarea matInput rows="4" formControlName="template"></textarea>
|
|
5064
|
+
</mat-form-field>
|
|
5065
|
+
}
|
|
5066
|
+
@if (group.controls.computeKind.value === 'expr') {
|
|
5067
|
+
<mat-form-field appearance="outline" class="span-3">
|
|
5068
|
+
<mat-label>Expression</mat-label>
|
|
5069
|
+
<input matInput formControlName="expression" />
|
|
5070
|
+
</mat-form-field>
|
|
5071
|
+
}
|
|
5072
|
+
@if (group.controls.computeKind.value === 'transformer') {
|
|
5073
|
+
<mat-form-field appearance="outline">
|
|
5074
|
+
<mat-label>Transformer</mat-label>
|
|
5075
|
+
<input matInput formControlName="transformerId" />
|
|
5076
|
+
</mat-form-field>
|
|
5077
|
+
<mat-form-field appearance="outline" class="span-2">
|
|
5078
|
+
<mat-label>Options (JSON)</mat-label>
|
|
5079
|
+
<textarea matInput rows="4" formControlName="options"></textarea>
|
|
5080
|
+
</mat-form-field>
|
|
5081
|
+
}
|
|
5082
|
+
<mat-form-field appearance="outline" class="span-3">
|
|
5083
|
+
<mat-label>Descrição</mat-label>
|
|
5084
|
+
<input matInput formControlName="description" />
|
|
5085
|
+
</mat-form-field>
|
|
5086
|
+
</div>
|
|
5087
|
+
</div>
|
|
5088
|
+
}
|
|
5089
|
+
</div>
|
|
5090
|
+
</div>
|
|
5091
|
+
</div>
|
|
5092
|
+
@if (stateError()) {
|
|
5093
|
+
<div class="page-error" role="alert">{{ stateError() }}</div>
|
|
5094
|
+
}
|
|
5095
|
+
|
|
4271
5096
|
<div class="page-actions">
|
|
4272
5097
|
<button mat-flat-button color="primary" type="button" (click)="apply()">Aplicar</button>
|
|
4273
5098
|
</div>
|
|
4274
5099
|
</div>
|
|
4275
|
-
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".page-config{display:grid;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.page-head{display:flex;align-items:center;gap:12px}.page-title{font-weight:600}.spacer{flex:1}.page-identity{display:grid;gap:4px;font-size:13px;color:var(--md-sys-color-on-surface-variant)}.page-section{font-weight:600;margin-top:8px}.grid-form{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.page-actions{display:flex;justify-content:flex-end}.page-error{color:var(--md-sys-color-error);font-size:13px}.page-config .mat-mdc-form-field{width:100%;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.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"] }]
|
|
5100
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".page-config{display:grid;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.page-head{display:flex;align-items:center;gap:12px}.page-title{font-weight:600}.spacer{flex:1}.page-identity{display:grid;gap:4px;font-size:13px;color:var(--md-sys-color-on-surface-variant)}.page-section{font-weight:600;margin-top:8px}.grid-form{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.state-grid{display:grid;gap:16px}.state-values,.state-group{padding:14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:14px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-surface-container-low) 92%,transparent),transparent),var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface))}.state-group{display:grid;gap:12px}.state-group-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.state-group-title{font-weight:600}.state-group-help{color:var(--md-sys-color-on-surface-variant);font-size:13px}.state-list{display:grid;gap:12px}.state-card{display:grid;gap:12px;padding:12px;border-radius:12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 80%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container) 84%,transparent)}.state-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.state-card-title{font-weight:600}.state-card-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.span-2{grid-column:span 2}.span-3{grid-column:1 / -1}.state-empty{color:var(--md-sys-color-on-surface-variant);font-size:13px}.state-operator-hint{display:flex;align-items:center;gap:8px;min-height:48px;padding:0 12px;border-radius:12px;color:var(--md-sys-color-on-surface-variant);background:color-mix(in srgb,var(--md-sys-color-secondary-container) 35%,transparent)}.state-operator-hint mat-icon{color:var(--md-sys-color-secondary)}.page-actions{display:flex;justify-content:flex-end}.page-error{color:var(--md-sys-color-error);font-size:13px}.page-config .mat-mdc-form-field{width:100%;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.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}@media(max-width:720px){.span-2,.span-3{grid-column:auto}.state-group-head{align-items:stretch;flex-direction:column}}\n"] }]
|
|
4276
5101
|
}], ctorParameters: () => [], propDecorators: { page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }], identity: [{ type: i0.Input, args: [{ isSignal: true, alias: "identity", required: false }] }], gridsterOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridsterOptions", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }] } });
|
|
4277
5102
|
|
|
4278
5103
|
class PageBuilderAiAdapter {
|
|
@@ -4520,13 +5345,15 @@ class PageBuilderAiAdapter {
|
|
|
4520
5345
|
connectionKey(connection) {
|
|
4521
5346
|
if (!connection)
|
|
4522
5347
|
return null;
|
|
4523
|
-
const
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
const
|
|
4527
|
-
|
|
5348
|
+
const from = 'widget' in connection.from
|
|
5349
|
+
? (connection.from.widget && connection.from.output ? `widget:${connection.from.widget}::${connection.from.output}` : null)
|
|
5350
|
+
: (connection.from.state ? `state:${connection.from.state}` : null);
|
|
5351
|
+
const to = 'widget' in connection.to
|
|
5352
|
+
? (connection.to.widget && connection.to.input ? `widget:${connection.to.widget}::${connection.to.input}` : null)
|
|
5353
|
+
: (connection.to.state ? `state:${connection.to.state}` : null);
|
|
5354
|
+
if (!from || !to)
|
|
4528
5355
|
return null;
|
|
4529
|
-
return `${
|
|
5356
|
+
return `${from}->${to}`;
|
|
4530
5357
|
}
|
|
4531
5358
|
gridsterFromPageOptions(options) {
|
|
4532
5359
|
if (!options)
|
|
@@ -4864,7 +5691,11 @@ class DynamicGridsterPageComponent {
|
|
|
4864
5691
|
this.widgets.set(widgets);
|
|
4865
5692
|
const parsed = this.parsePage(this.page) || { widgets };
|
|
4866
5693
|
// Remove connections referencing this widget
|
|
4867
|
-
const conns = (parsed.connections || []).filter((c) =>
|
|
5694
|
+
const conns = (parsed.connections || []).filter((c) => {
|
|
5695
|
+
const fromMatches = c?.from && 'widget' in c.from && c.from.widget === key;
|
|
5696
|
+
const toMatches = c?.to && 'widget' in c.to && c.to.widget === key;
|
|
5697
|
+
return !fromMatches && !toMatches;
|
|
5698
|
+
});
|
|
4868
5699
|
const next = { ...parsed, widgets, connections: conns };
|
|
4869
5700
|
this.page = next;
|
|
4870
5701
|
this.pageChange.emit(next);
|
|
@@ -5016,6 +5847,8 @@ class DynamicGridsterPageComponent {
|
|
|
5016
5847
|
const matches = this.conn.findMatches(p?.connections, fromKey, evt.output);
|
|
5017
5848
|
let widgets = [...this.widgets()];
|
|
5018
5849
|
for (const c of matches) {
|
|
5850
|
+
if (!('widget' in c.to))
|
|
5851
|
+
continue;
|
|
5019
5852
|
widgets = this.conn.applyMatch(widgets, c, evt);
|
|
5020
5853
|
}
|
|
5021
5854
|
this.widgets.set(widgets);
|