@praxisui/page-builder 9.0.23 → 9.0.25

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.
@@ -7,7 +7,7 @@ import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
7
7
  import * as i2$1 from '@angular/material/icon';
8
8
  import { MatIconModule } from '@angular/material/icon';
9
9
  import * as i2$2 from '@praxisui/core';
10
- import { PraxisIconDirective, BUILTIN_SHELL_PRESETS, PraxisIconButtonComponent, providePraxisI18n, PraxisI18nService, GLOBAL_ACTION_CATALOG, getGlobalActionCatalog, PRAXIS_GLOBAL_ACTION_CATALOG, SurfaceOpenActionEditorComponent, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, NestedWidgetConfigAccessor, deepMerge, generateId, ComponentMetadataRegistry, ObservabilityDashboardService, PraxisRuntimeComponentObservationRegistryService, domainKnowledgeTimelineToRichContentDocument, DomainRuleService, DomainKnowledgeService, SETTINGS_PANEL_BRIDGE, DynamicWidgetPageComponent, DYNAMIC_PAGE_SHELL_EDITOR } from '@praxisui/core';
10
+ import { PraxisIconDirective, providePraxisI18n, BUILTIN_SHELL_PRESETS, PraxisIconButtonComponent, PraxisI18nService, GLOBAL_ACTION_CATALOG, getGlobalActionCatalog, PRAXIS_GLOBAL_ACTION_CATALOG, SurfaceOpenActionEditorComponent, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, NestedWidgetConfigAccessor, deepMerge, generateId, ComponentMetadataRegistry, ObservabilityDashboardService, PraxisRuntimeComponentObservationRegistryService, domainKnowledgeTimelineToRichContentDocument, DomainRuleService, DomainKnowledgeService, SETTINGS_PANEL_BRIDGE, DynamicWidgetPageComponent, DYNAMIC_PAGE_SHELL_EDITOR } from '@praxisui/core';
11
11
  import * as i3 from '@angular/material/tooltip';
12
12
  import { MatTooltipModule } from '@angular/material/tooltip';
13
13
  import * as i3$1 from '@angular/material/form-field';
@@ -184,6 +184,11 @@ class FloatingToolbarComponent {
184
184
  previewActive = false;
185
185
  connectionMode = false;
186
186
  modeLabel = '';
187
+ addLabel = 'Insert component';
188
+ saveLabel = 'Save page';
189
+ undoLabel = 'Undo';
190
+ redoLabel = 'Redo';
191
+ settingsLabel = 'Page settings';
187
192
  previewLabel = 'Pre-visualizar';
188
193
  exitPreviewLabel = 'Voltar para edicao';
189
194
  add = new EventEmitter();
@@ -193,7 +198,7 @@ class FloatingToolbarComponent {
193
198
  preview = new EventEmitter();
194
199
  save = new EventEmitter();
195
200
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FloatingToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
196
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: FloatingToolbarComponent, isStandalone: true, selector: "praxis-floating-toolbar", inputs: { visible: "visible", canUndo: "canUndo", canRedo: "canRedo", showSave: "showSave", showPreview: "showPreview", editingEnabled: "editingEnabled", previewActive: "previewActive", connectionMode: "connectionMode", modeLabel: "modeLabel", previewLabel: "previewLabel", exitPreviewLabel: "exitPreviewLabel" }, outputs: { add: "add", undo: "undo", redo: "redo", settings: "settings", preview: "preview", save: "save" }, ngImport: i0, template: `
201
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: FloatingToolbarComponent, isStandalone: true, selector: "praxis-floating-toolbar", inputs: { visible: "visible", canUndo: "canUndo", canRedo: "canRedo", showSave: "showSave", showPreview: "showPreview", editingEnabled: "editingEnabled", previewActive: "previewActive", connectionMode: "connectionMode", modeLabel: "modeLabel", addLabel: "addLabel", saveLabel: "saveLabel", undoLabel: "undoLabel", redoLabel: "redoLabel", settingsLabel: "settingsLabel", previewLabel: "previewLabel", exitPreviewLabel: "exitPreviewLabel" }, outputs: { add: "add", undo: "undo", redo: "redo", settings: "settings", preview: "preview", save: "save" }, ngImport: i0, template: `
197
202
  @if (visible) {
198
203
  <div
199
204
  class="pdx-floating-toolbar"
@@ -204,25 +209,25 @@ class FloatingToolbarComponent {
204
209
  <span class="pdx-mode-chip" [attr.data-testid]="'page-builder-toolbar-mode'">{{ modeLabel }}</span>
205
210
  }
206
211
  <div class="pdx-actions">
207
- <button mat-mini-fab color="primary" (click)="add.emit()" [disabled]="!editingEnabled" [matTooltip]="'Inserir componente'" aria-label="Inserir componente">
212
+ <button mat-mini-fab color="primary" (click)="add.emit()" [disabled]="!editingEnabled" [matTooltip]="addLabel" [attr.aria-label]="addLabel">
208
213
  <mat-icon [praxisIcon]="'add'"></mat-icon>
209
214
  </button>
210
215
  @if (showSave) {
211
- <button mat-mini-fab color="accent" (click)="save.emit()" [disabled]="!editingEnabled" [matTooltip]="'Salvar pagina'" aria-label="Salvar pagina">
216
+ <button mat-mini-fab color="accent" (click)="save.emit()" [disabled]="!editingEnabled" [matTooltip]="saveLabel" [attr.aria-label]="saveLabel">
212
217
  <mat-icon [praxisIcon]="'save'"></mat-icon>
213
218
  </button>
214
219
  }
215
220
  @if (canUndo) {
216
- <button mat-mini-fab (click)="undo.emit()" [disabled]="!editingEnabled" [matTooltip]="'Desfazer'" aria-label="Desfazer">
221
+ <button mat-mini-fab (click)="undo.emit()" [disabled]="!editingEnabled" [matTooltip]="undoLabel" [attr.aria-label]="undoLabel">
217
222
  <mat-icon [praxisIcon]="'undo'"></mat-icon>
218
223
  </button>
219
224
  }
220
225
  @if (canRedo) {
221
- <button mat-mini-fab (click)="redo.emit()" [disabled]="!editingEnabled" [matTooltip]="'Refazer'" aria-label="Refazer">
226
+ <button mat-mini-fab (click)="redo.emit()" [disabled]="!editingEnabled" [matTooltip]="redoLabel" [attr.aria-label]="redoLabel">
222
227
  <mat-icon [praxisIcon]="'redo'"></mat-icon>
223
228
  </button>
224
229
  }
225
- <button mat-mini-fab (click)="settings.emit()" [disabled]="!editingEnabled" [matTooltip]="'Configuracoes da pagina'" aria-label="Configuracoes da pagina">
230
+ <button mat-mini-fab (click)="settings.emit()" [disabled]="!editingEnabled" [matTooltip]="settingsLabel" [attr.aria-label]="settingsLabel">
226
231
  <mat-icon [praxisIcon]="'settings'"></mat-icon>
227
232
  </button>
228
233
  @if (showPreview) {
@@ -256,25 +261,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
256
261
  <span class="pdx-mode-chip" [attr.data-testid]="'page-builder-toolbar-mode'">{{ modeLabel }}</span>
257
262
  }
258
263
  <div class="pdx-actions">
259
- <button mat-mini-fab color="primary" (click)="add.emit()" [disabled]="!editingEnabled" [matTooltip]="'Inserir componente'" aria-label="Inserir componente">
264
+ <button mat-mini-fab color="primary" (click)="add.emit()" [disabled]="!editingEnabled" [matTooltip]="addLabel" [attr.aria-label]="addLabel">
260
265
  <mat-icon [praxisIcon]="'add'"></mat-icon>
261
266
  </button>
262
267
  @if (showSave) {
263
- <button mat-mini-fab color="accent" (click)="save.emit()" [disabled]="!editingEnabled" [matTooltip]="'Salvar pagina'" aria-label="Salvar pagina">
268
+ <button mat-mini-fab color="accent" (click)="save.emit()" [disabled]="!editingEnabled" [matTooltip]="saveLabel" [attr.aria-label]="saveLabel">
264
269
  <mat-icon [praxisIcon]="'save'"></mat-icon>
265
270
  </button>
266
271
  }
267
272
  @if (canUndo) {
268
- <button mat-mini-fab (click)="undo.emit()" [disabled]="!editingEnabled" [matTooltip]="'Desfazer'" aria-label="Desfazer">
273
+ <button mat-mini-fab (click)="undo.emit()" [disabled]="!editingEnabled" [matTooltip]="undoLabel" [attr.aria-label]="undoLabel">
269
274
  <mat-icon [praxisIcon]="'undo'"></mat-icon>
270
275
  </button>
271
276
  }
272
277
  @if (canRedo) {
273
- <button mat-mini-fab (click)="redo.emit()" [disabled]="!editingEnabled" [matTooltip]="'Refazer'" aria-label="Refazer">
278
+ <button mat-mini-fab (click)="redo.emit()" [disabled]="!editingEnabled" [matTooltip]="redoLabel" [attr.aria-label]="redoLabel">
274
279
  <mat-icon [praxisIcon]="'redo'"></mat-icon>
275
280
  </button>
276
281
  }
277
- <button mat-mini-fab (click)="settings.emit()" [disabled]="!editingEnabled" [matTooltip]="'Configuracoes da pagina'" aria-label="Configuracoes da pagina">
282
+ <button mat-mini-fab (click)="settings.emit()" [disabled]="!editingEnabled" [matTooltip]="settingsLabel" [attr.aria-label]="settingsLabel">
278
283
  <mat-icon [praxisIcon]="'settings'"></mat-icon>
279
284
  </button>
280
285
  @if (showPreview) {
@@ -312,6 +317,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
312
317
  type: Input
313
318
  }], modeLabel: [{
314
319
  type: Input
320
+ }], addLabel: [{
321
+ type: Input
322
+ }], saveLabel: [{
323
+ type: Input
324
+ }], undoLabel: [{
325
+ type: Input
326
+ }], redoLabel: [{
327
+ type: Input
328
+ }], settingsLabel: [{
329
+ type: Input
315
330
  }], previewLabel: [{
316
331
  type: Input
317
332
  }], exitPreviewLabel: [{
@@ -330,2032 +345,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
330
345
  type: Output
331
346
  }] } });
332
347
 
333
- class ComponentPaletteDialogComponent {
334
- dialogRef;
335
- registry;
336
- data;
337
- query = signal('', ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
338
- all = signal([], ...(ngDevMode ? [{ debugName: "all" }] : /* istanbul ignore next */ []));
339
- filtered = computed(() => this.applyFilters(), ...(ngDevMode ? [{ debugName: "filtered" }] : /* istanbul ignore next */ []));
340
- density = computed(() => {
341
- const n = this.filtered().length;
342
- if (n <= 4)
343
- return 'roomy';
344
- if (n > 12)
345
- return 'dense';
346
- return 'normal';
347
- }, ...(ngDevMode ? [{ debugName: "density" }] : /* istanbul ignore next */ []));
348
- constructor(dialogRef, registry, data) {
349
- this.dialogRef = dialogRef;
350
- this.registry = registry;
351
- this.data = data;
352
- }
353
- ngOnInit() {
354
- this.all.set(this.buildEntries(this.registry.getAll()));
355
- }
356
- select(entry) {
357
- this.dialogRef.close({
358
- componentId: entry.componentId,
359
- ...(entry.presetId ? { presetId: entry.presetId } : {}),
360
- ...(entry.inputs ? { inputs: entry.inputs } : {}),
361
- });
362
- }
363
- trackById = (_, entry) => entry.key;
364
- getPreview(entry) {
365
- return entry.description;
366
- }
367
- // Create effect in injection context (field initializer is allowed)
368
- _adjustSizeEffect = effect(() => {
369
- const n = this.filtered().length;
370
- let width = '720px';
371
- if (n <= 4)
372
- width = '560px';
373
- else if (n <= 8)
374
- width = '680px';
375
- const container = this.dialogRef._containerInstance;
376
- if (container && container._config) {
377
- container._config.width = width;
378
- }
379
- }, ...(ngDevMode ? [{ debugName: "_adjustSizeEffect" }] : /* istanbul ignore next */ []));
380
- applyFilters() {
381
- const list = (this.all() || []);
382
- const q = (this.query() || '').toLowerCase();
383
- const allowIds = new Set((this.data?.allowedWidgetIds || []).map((s) => s.toLowerCase()));
384
- const allowTags = new Set((this.data?.allowedWidgetTags || []).map((s) => s.toLowerCase()));
385
- const allowPresets = new Set((this.data?.allowedPresetIds || []).map((s) => s.toLowerCase()));
386
- const metadataById = new Map(this.registry.getAll().map((meta) => [meta.id, meta]));
387
- return list.filter((entry) => {
388
- const meta = metadataById.get(entry.componentId);
389
- if (!meta)
390
- return false;
391
- if (q && !entry.searchText.includes(q)) {
392
- return false;
393
- }
394
- if (allowIds.size > 0 && !allowIds.has((meta.id || '').toLowerCase()))
395
- return false;
396
- if (allowTags.size > 0 && !meta.tags?.some(t => allowTags.has(t.toLowerCase())))
397
- return false;
398
- if (entry.presetId && allowPresets.size > 0 && !allowPresets.has(entry.presetId.toLowerCase()))
399
- return false;
400
- if (typeof this.data?.predicate === 'function' && !this.data.predicate(meta))
401
- return false;
402
- return true;
403
- });
404
- }
405
- buildEntries(metadata) {
406
- return metadata.flatMap((meta) => {
407
- const baseEntry = {
408
- key: meta.id,
409
- componentId: meta.id,
410
- title: meta.friendlyName || meta.id,
411
- description: meta.description || meta.selector,
412
- icon: meta.icon || 'widgets',
413
- badges: this.buildBadges(meta, false),
414
- searchText: `${meta.id} ${meta.friendlyName || ''} ${meta.description || ''} ${meta.selector || ''} ${this.buildBadgeSearchText(meta, false)}`.toLowerCase(),
415
- };
416
- const presets = (meta.insertionPresets ?? []).map((preset) => ({
417
- key: `${meta.id}::${preset.id}`,
418
- componentId: meta.id,
419
- presetId: preset.id,
420
- title: `${meta.friendlyName || meta.id} · ${preset.label}`,
421
- description: preset.description || meta.description || meta.selector,
422
- icon: preset.icon || meta.icon || 'widgets',
423
- badges: this.buildBadges(meta, true),
424
- searchText: `${meta.id} ${meta.friendlyName || ''} ${preset.label} ${preset.description || ''} ${meta.description || ''} ${this.buildBadgeSearchText(meta, true)}`.toLowerCase(),
425
- inputs: preset.inputs ? JSON.parse(JSON.stringify(preset.inputs)) : undefined,
426
- }));
427
- return [baseEntry, ...presets];
428
- });
429
- }
430
- buildBadges(meta, isPreset) {
431
- const badges = [];
432
- const tags = new Set((meta.tags ?? []).map((tag) => tag.toLowerCase()));
433
- if (meta.configEditor?.component) {
434
- badges.push({
435
- label: 'Config editor',
436
- icon: 'tune',
437
- tone: 'ready',
438
- description: 'This component publishes a canonical settings-panel editor for its input contract.',
439
- });
440
- }
441
- if (meta.authoringManifestRef?.componentId) {
442
- badges.push({
443
- label: 'AI authoring',
444
- icon: 'auto_awesome',
445
- tone: 'ready',
446
- description: 'This component owner publishes a canonical AI authoring manifest for governed changes.',
447
- });
448
- }
449
- if (meta.ports?.length || meta.actions?.length || meta.commands?.length) {
450
- badges.push({
451
- label: 'Governed',
452
- icon: 'account_tree',
453
- tone: 'ready',
454
- description: 'This component exposes semantic ports, actions, or commands for governed composition.',
455
- });
456
- }
457
- if (isPreset) {
458
- badges.push({
459
- label: 'Preset',
460
- icon: 'auto_awesome_motion',
461
- tone: 'info',
462
- description: 'This entry inserts a curated preset without changing the component contract.',
463
- });
464
- }
465
- if (this.hasDataSourceInputs(meta) || tags.has('data') || tags.has('analytics')) {
466
- badges.push({
467
- label: 'Data source',
468
- icon: 'database',
469
- tone: 'info',
470
- description: 'This component commonly needs schema, API, resource, or dataset context.',
471
- });
472
- }
473
- if (tags.has('deprecated') || tags.has('legacy') || /legacy|legado|deprecated/i.test(meta.description || '')) {
474
- badges.push({
475
- label: 'Legacy',
476
- icon: 'history',
477
- tone: 'warning',
478
- description: 'This entry is retained for compatibility and should not be the default for new canonical authoring.',
479
- });
480
- }
481
- if (tags.has('demo') || tags.has('host') || /host-only|host|demo/i.test(`${meta.friendlyName} ${meta.description || ''}`)) {
482
- badges.push({
483
- label: 'Host/demo',
484
- icon: 'science',
485
- tone: 'neutral',
486
- description: 'This entry depends on host/demo semantics and may need governance before public reuse.',
487
- });
488
- }
489
- return badges;
490
- }
491
- buildBadgeSearchText(meta, isPreset) {
492
- return this.buildBadges(meta, isPreset)
493
- .map((badge) => `${badge.label} ${badge.description}`)
494
- .join(' ');
495
- }
496
- hasDataSourceInputs(meta) {
497
- return (meta.inputs ?? []).some((input) => /resource|schema|api|data(source)?|endpoint|resolver/i.test(input.name));
498
- }
499
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ComponentPaletteDialogComponent, deps: [{ token: i1.MatDialogRef }, { token: i2$2.ComponentMetadataRegistry }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component });
500
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: ComponentPaletteDialogComponent, isStandalone: true, selector: "praxis-component-palette-dialog", ngImport: i0, template: `
501
- <h2 mat-dialog-title>
502
- <div class="dlg-title">
503
- <mat-icon class="dlg-icon" [praxisIcon]="'widgets'"></mat-icon>
504
- <div class="dlg-texts">
505
- <div class="dlg-head">{{ data?.title || 'Inserir componente' }}</div>
506
- <div class="dlg-sub">Escolha um componente para adicionar à página · {{ filtered().length }} disponíveis</div>
507
- </div>
508
- </div>
509
- </h2>
510
- <div mat-dialog-content class="pdx-palette-content">
511
- <mat-form-field class="pdx-palette-search" appearance="outline">
512
- <mat-label>Buscar componente</mat-label>
513
- <mat-icon matPrefix [praxisIcon]="'search'"></mat-icon>
514
- <input
515
- matInput
516
- type="search"
517
- [ngModel]="query()"
518
- (ngModelChange)="query.set($event)"
519
- placeholder="Nome, capability ou readiness"
520
- data-testid="component-palette-search"
521
- />
522
- </mat-form-field>
523
- @if (filtered().length) {
524
- <div [class]="'pdx-grid ' + density()">
525
- @for (entry of filtered(); track trackById($index, entry)) {
526
- <button
527
- class="pdx-card mat-elevation-z2"
528
- type="button"
529
- [attr.aria-label]="'Adicionar ' + entry.title"
530
- (click)="select(entry)"
531
- >
532
- <div class="pdx-card-icon">
533
- <mat-icon [praxisIcon]="entry.icon"></mat-icon>
534
- </div>
535
- <div class="pdx-card-body">
536
- <div class="pdx-card-title">{{ entry.title }}</div>
537
- <div class="pdx-card-desc">{{ entry.description }}</div>
538
- @if (entry.badges.length) {
539
- <div class="pdx-card-badges">
540
- @for (badge of entry.badges; track badge) {
541
- <span
542
- class="pdx-card-badge"
543
- [class]="'pdx-card-badge ' + badge.tone"
544
- [title]="badge.description"
545
- >
546
- <mat-icon [praxisIcon]="badge.icon" aria-hidden="true"></mat-icon>
547
- {{ badge.label }}
548
- </span>
549
- }
550
- </div>
551
- }
552
- </div>
553
- </button>
554
- }
555
- </div>
556
- } @else {
557
- <div class="pdx-empty">
558
- <mat-icon [praxisIcon]="'sentiment_dissatisfied'"></mat-icon>
559
- <div>Nenhum componente disponível</div>
560
- </div>
561
- }
562
-
563
- </div>
564
- <div mat-dialog-actions align="end">
565
- <button mat-stroked-button color="primary" mat-dialog-close>Cancelar</button>
566
- </div>
567
- `, isInline: true, styles: [":host{display:block}h2[mat-dialog-title]{margin:0;padding:12px 24px 10px;border-bottom:1px solid var(--md-sys-color-outline-variant)}.dlg-title{display:flex;align-items:center;gap:10px}.dlg-icon{color:var(--md-sys-color-primary)}.dlg-head{font-size:18px;font-weight:600;line-height:1.2}.dlg-sub{font-size:12px;opacity:.85;color:var(--md-sys-color-on-surface-variant)}.pdx-palette-content{min-width:480px;max-width:920px;padding-top:8px}.pdx-palette-search{width:100%;margin-bottom:12px}.pdx-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px}.pdx-grid.dense{grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.pdx-grid.roomy{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px}.pdx-card{appearance:none;position:relative;display:grid;grid-template-columns:44px 1fr;grid-template-rows:auto auto;gap:10px;padding:14px;border:1px solid transparent;border-radius:14px;cursor:pointer;outline:none;color:inherit;font:inherit;min-height:92px;text-align:left;background:var(--md-sys-color-surface);border-color:var(--md-sys-color-outline-variant);transition:box-shadow .2s ease,background .25s ease,transform .06s ease}.pdx-grid.dense .pdx-card{padding:10px;border-radius:12px;min-height:84px}.pdx-grid.roomy .pdx-card{padding:16px;border-radius:16px;min-height:100px}.pdx-card:focus,.pdx-card:hover{box-shadow:var(--mat-elevation-level4);background:var(--md-sys-color-surface-container-low);border-color:var(--md-sys-color-primary)}.pdx-card:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-card:active{transform:translateY(1px)}.pdx-card-icon{grid-row:span 2;display:flex;align-items:center;justify-content:center;color:var(--md-sys-color-primary)}.pdx-card-title{font-weight:600;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pdx-card-desc{color:var(--md-sys-color-on-surface-variant);display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;word-break:break-word}.pdx-card-badges{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}.pdx-card-badge{display:inline-flex;align-items:center;gap:4px;min-height:22px;padding:2px 8px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface-variant);background:var(--md-sys-color-surface-container-highest);font-size:11px;line-height:1.2;font-weight:600;letter-spacing:.01em}.pdx-card-badge mat-icon{width:14px;height:14px;font-size:14px}.pdx-card-badge.ready{color:var(--md-sys-color-primary);border-color:color-mix(in srgb,var(--md-sys-color-primary) 45%,transparent);background:color-mix(in srgb,var(--md-sys-color-primary-container) 72%,transparent)}.pdx-card-badge.info{color:var(--md-sys-color-tertiary);border-color:color-mix(in srgb,var(--md-sys-color-tertiary) 38%,transparent);background:color-mix(in srgb,var(--md-sys-color-tertiary-container) 68%,transparent)}.pdx-card-badge.warning{color:var(--md-sys-color-error);border-color:color-mix(in srgb,var(--md-sys-color-error) 35%,transparent);background:color-mix(in srgb,var(--md-sys-color-error-container) 62%,transparent)}.pdx-empty{padding:24px;display:grid;place-items:center;color:var(--md-sys-color-on-surface-variant);gap:8px}@media(max-width:600px){.pdx-palette-content{min-width:320px}.pdx-grid{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.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: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
568
- }
569
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ComponentPaletteDialogComponent, decorators: [{
570
- type: Component,
571
- args: [{ selector: 'praxis-component-palette-dialog', standalone: true, imports: [FormsModule, MatDialogModule, MatFormFieldModule, MatInputModule, MatIconModule, MatButtonModule, PraxisIconDirective], template: `
572
- <h2 mat-dialog-title>
573
- <div class="dlg-title">
574
- <mat-icon class="dlg-icon" [praxisIcon]="'widgets'"></mat-icon>
575
- <div class="dlg-texts">
576
- <div class="dlg-head">{{ data?.title || 'Inserir componente' }}</div>
577
- <div class="dlg-sub">Escolha um componente para adicionar à página · {{ filtered().length }} disponíveis</div>
578
- </div>
579
- </div>
580
- </h2>
581
- <div mat-dialog-content class="pdx-palette-content">
582
- <mat-form-field class="pdx-palette-search" appearance="outline">
583
- <mat-label>Buscar componente</mat-label>
584
- <mat-icon matPrefix [praxisIcon]="'search'"></mat-icon>
585
- <input
586
- matInput
587
- type="search"
588
- [ngModel]="query()"
589
- (ngModelChange)="query.set($event)"
590
- placeholder="Nome, capability ou readiness"
591
- data-testid="component-palette-search"
592
- />
593
- </mat-form-field>
594
- @if (filtered().length) {
595
- <div [class]="'pdx-grid ' + density()">
596
- @for (entry of filtered(); track trackById($index, entry)) {
597
- <button
598
- class="pdx-card mat-elevation-z2"
599
- type="button"
600
- [attr.aria-label]="'Adicionar ' + entry.title"
601
- (click)="select(entry)"
602
- >
603
- <div class="pdx-card-icon">
604
- <mat-icon [praxisIcon]="entry.icon"></mat-icon>
605
- </div>
606
- <div class="pdx-card-body">
607
- <div class="pdx-card-title">{{ entry.title }}</div>
608
- <div class="pdx-card-desc">{{ entry.description }}</div>
609
- @if (entry.badges.length) {
610
- <div class="pdx-card-badges">
611
- @for (badge of entry.badges; track badge) {
612
- <span
613
- class="pdx-card-badge"
614
- [class]="'pdx-card-badge ' + badge.tone"
615
- [title]="badge.description"
616
- >
617
- <mat-icon [praxisIcon]="badge.icon" aria-hidden="true"></mat-icon>
618
- {{ badge.label }}
619
- </span>
620
- }
621
- </div>
622
- }
623
- </div>
624
- </button>
625
- }
626
- </div>
627
- } @else {
628
- <div class="pdx-empty">
629
- <mat-icon [praxisIcon]="'sentiment_dissatisfied'"></mat-icon>
630
- <div>Nenhum componente disponível</div>
631
- </div>
632
- }
633
-
634
- </div>
635
- <div mat-dialog-actions align="end">
636
- <button mat-stroked-button color="primary" mat-dialog-close>Cancelar</button>
637
- </div>
638
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}h2[mat-dialog-title]{margin:0;padding:12px 24px 10px;border-bottom:1px solid var(--md-sys-color-outline-variant)}.dlg-title{display:flex;align-items:center;gap:10px}.dlg-icon{color:var(--md-sys-color-primary)}.dlg-head{font-size:18px;font-weight:600;line-height:1.2}.dlg-sub{font-size:12px;opacity:.85;color:var(--md-sys-color-on-surface-variant)}.pdx-palette-content{min-width:480px;max-width:920px;padding-top:8px}.pdx-palette-search{width:100%;margin-bottom:12px}.pdx-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px}.pdx-grid.dense{grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.pdx-grid.roomy{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px}.pdx-card{appearance:none;position:relative;display:grid;grid-template-columns:44px 1fr;grid-template-rows:auto auto;gap:10px;padding:14px;border:1px solid transparent;border-radius:14px;cursor:pointer;outline:none;color:inherit;font:inherit;min-height:92px;text-align:left;background:var(--md-sys-color-surface);border-color:var(--md-sys-color-outline-variant);transition:box-shadow .2s ease,background .25s ease,transform .06s ease}.pdx-grid.dense .pdx-card{padding:10px;border-radius:12px;min-height:84px}.pdx-grid.roomy .pdx-card{padding:16px;border-radius:16px;min-height:100px}.pdx-card:focus,.pdx-card:hover{box-shadow:var(--mat-elevation-level4);background:var(--md-sys-color-surface-container-low);border-color:var(--md-sys-color-primary)}.pdx-card:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-card:active{transform:translateY(1px)}.pdx-card-icon{grid-row:span 2;display:flex;align-items:center;justify-content:center;color:var(--md-sys-color-primary)}.pdx-card-title{font-weight:600;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pdx-card-desc{color:var(--md-sys-color-on-surface-variant);display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;word-break:break-word}.pdx-card-badges{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}.pdx-card-badge{display:inline-flex;align-items:center;gap:4px;min-height:22px;padding:2px 8px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface-variant);background:var(--md-sys-color-surface-container-highest);font-size:11px;line-height:1.2;font-weight:600;letter-spacing:.01em}.pdx-card-badge mat-icon{width:14px;height:14px;font-size:14px}.pdx-card-badge.ready{color:var(--md-sys-color-primary);border-color:color-mix(in srgb,var(--md-sys-color-primary) 45%,transparent);background:color-mix(in srgb,var(--md-sys-color-primary-container) 72%,transparent)}.pdx-card-badge.info{color:var(--md-sys-color-tertiary);border-color:color-mix(in srgb,var(--md-sys-color-tertiary) 38%,transparent);background:color-mix(in srgb,var(--md-sys-color-tertiary-container) 68%,transparent)}.pdx-card-badge.warning{color:var(--md-sys-color-error);border-color:color-mix(in srgb,var(--md-sys-color-error) 35%,transparent);background:color-mix(in srgb,var(--md-sys-color-error-container) 62%,transparent)}.pdx-empty{padding:24px;display:grid;place-items:center;color:var(--md-sys-color-on-surface-variant);gap:8px}@media(max-width:600px){.pdx-palette-content{min-width:320px}.pdx-grid{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}}\n"] }]
639
- }], ctorParameters: () => [{ type: i1.MatDialogRef }, { type: i2$2.ComponentMetadataRegistry }, { type: undefined, decorators: [{
640
- type: Inject,
641
- args: [MAT_DIALOG_DATA]
642
- }] }] });
643
-
644
- const BUILTIN_PRESET_LABELS = {
645
- 'dark-glass': 'Dark Glass',
646
- 'light-neutral': 'Light Neutral',
647
- graphite: 'Graphite',
648
- frameless: 'Frameless',
649
- 'data-panel': 'Data Panel',
650
- 'chart-panel': 'Chart Panel',
651
- 'metric-panel': 'Metric Panel',
652
- 'executive-card': 'Executive Card',
653
- 'filter-bar': 'Filter Bar',
654
- };
655
- const BUILTIN_PRESET_OPTIONS = Object.keys(BUILTIN_SHELL_PRESETS).map((id) => ({
656
- id,
657
- label: BUILTIN_PRESET_LABELS[id] ?? id,
658
- }));
659
- const BUILTIN_PRESET_MAP = BUILTIN_SHELL_PRESETS;
660
- const SET_INPUT_PRESETS = {
661
- 'praxis-dynamic-form': [
662
- {
663
- input: 'mode',
664
- label: 'Modo',
665
- icon: 'toggle_on',
666
- values: [
667
- { label: 'Visualização', value: 'view', icon: 'visibility' },
668
- { label: 'Edição', value: 'edit', icon: 'edit' },
669
- { label: 'Criação', value: 'create', icon: 'add' },
670
- ],
671
- },
672
- {
673
- input: 'schemaSource',
674
- label: 'Fonte do schema',
675
- icon: 'schema',
676
- values: [
677
- { label: 'Recurso', value: 'resource' },
678
- { label: 'Filtro', value: 'filter' },
679
- ],
680
- },
681
- {
682
- input: 'notifyIfOutdated',
683
- label: 'Notificação de schema',
684
- icon: 'notification_important',
685
- values: [
686
- { label: 'Inline', value: 'inline' },
687
- { label: 'Snackbar', value: 'snackbar' },
688
- { label: 'Ambos', value: 'both' },
689
- { label: 'Nenhum', value: 'none' },
690
- ],
691
- },
692
- {
693
- input: 'removeEmptyContainersOnSave',
694
- label: 'Remover containers vazios',
695
- icon: 'cleaning_services',
696
- values: [
697
- { label: 'Ativar', value: true },
698
- { label: 'Desativar', value: false },
699
- ],
700
- },
701
- {
702
- input: 'autoOpenSettingsOnOutdated',
703
- label: 'Abrir editor automático',
704
- icon: 'open_in_new',
705
- values: [
706
- { label: 'Ativar', value: true },
707
- { label: 'Desativar', value: false },
708
- ],
709
- },
710
- {
711
- input: 'readonlyModeGlobal',
712
- label: 'Somente leitura global',
713
- icon: 'lock',
714
- values: [
715
- { label: 'Ativar', value: true },
716
- { label: 'Desativar', value: false },
717
- ],
718
- },
719
- {
720
- input: 'disabledModeGlobal',
721
- label: 'Desabilitar global',
722
- icon: 'block',
723
- values: [
724
- { label: 'Ativar', value: true },
725
- { label: 'Desativar', value: false },
726
- ],
727
- },
728
- {
729
- input: 'presentationModeGlobal',
730
- label: 'Modo apresentação',
731
- icon: 'slideshow',
732
- values: [
733
- { label: 'Ativar', value: true },
734
- { label: 'Desativar', value: false },
735
- ],
736
- },
737
- {
738
- input: 'visibleGlobal',
739
- label: 'Visibilidade global',
740
- icon: 'visibility',
741
- values: [
742
- { label: 'Exibir', value: true },
743
- { label: 'Ocultar', value: false },
744
- ],
745
- },
746
- ],
747
- 'praxis-table': [
748
- {
749
- input: 'showToolbar',
750
- label: 'Toolbar',
751
- icon: 'toolbar',
752
- values: [
753
- { label: 'Exibir', value: true },
754
- { label: 'Ocultar', value: false },
755
- ],
756
- },
757
- {
758
- input: 'toolbarV2',
759
- label: 'Toolbar v2',
760
- icon: 'view_compact',
761
- values: [
762
- { label: 'Ativar', value: true },
763
- { label: 'Desativar', value: false },
764
- ],
765
- },
766
- {
767
- input: 'autoDelete',
768
- label: 'Auto delete',
769
- icon: 'delete_forever',
770
- values: [
771
- { label: 'Ativar', value: true },
772
- { label: 'Desativar', value: false },
773
- ],
774
- },
775
- {
776
- input: 'dense',
777
- label: 'Modo compacto',
778
- icon: 'compress',
779
- values: [
780
- { label: 'Ativar', value: true },
781
- { label: 'Desativar', value: false },
782
- ],
783
- },
784
- ],
785
- 'praxis-stepper': [
786
- {
787
- input: 'inputs.config.orientation',
788
- label: 'Orientação',
789
- icon: 'swap_vert',
790
- values: [
791
- { label: 'Horizontal', value: 'horizontal' },
792
- { label: 'Vertical', value: 'vertical' },
793
- ],
794
- },
795
- {
796
- input: 'inputs.config.headerPosition',
797
- label: 'Posição do cabeçalho',
798
- icon: 'vertical_align_top',
799
- values: [
800
- { label: 'Topo', value: 'top' },
801
- { label: 'Base', value: 'bottom' },
802
- ],
803
- },
804
- {
805
- input: 'inputs.config.linear',
806
- label: 'Modo linear',
807
- icon: 'linear_scale',
808
- values: [
809
- { label: 'Ativar', value: true },
810
- { label: 'Desativar', value: false },
811
- ],
812
- },
813
- {
814
- input: 'inputs.config.navigation.variant',
815
- label: 'Navegação (variante)',
816
- icon: 'tune',
817
- values: [
818
- { label: 'Basic', value: 'basic' },
819
- { label: 'Flat', value: 'flat' },
820
- { label: 'Stroked', value: 'stroked' },
821
- { label: 'Raised', value: 'raised' },
822
- ],
823
- },
824
- {
825
- input: 'inputs.config.navigation.align',
826
- label: 'Navegação (alinhamento)',
827
- icon: 'format_align_center',
828
- values: [
829
- { label: 'Início', value: 'start' },
830
- { label: 'Centro', value: 'center' },
831
- { label: 'Fim', value: 'end' },
832
- { label: 'Espaçado', value: 'space-between' },
833
- ],
834
- },
835
- {
836
- input: 'labelPosition',
837
- label: 'Posição do rótulo',
838
- icon: 'format_align_center',
839
- values: [
840
- { label: 'Abaixo', value: 'bottom' },
841
- { label: 'Ao lado', value: 'end' },
842
- ],
843
- },
844
- {
845
- input: 'density',
846
- label: 'Densidade',
847
- icon: 'density_small',
848
- values: [
849
- { label: 'Padrão', value: 'default' },
850
- { label: 'Confortável', value: 'comfortable' },
851
- { label: 'Compacto', value: 'compact' },
852
- ],
853
- },
854
- ],
855
- 'praxis-expansion': [
856
- {
857
- input: 'strictValidation',
858
- label: 'Validação estrita',
859
- icon: 'rule',
860
- values: [
861
- { label: 'Ativar', value: true },
862
- { label: 'Desativar', value: false },
863
- ],
864
- },
865
- ],
866
- };
867
- class WidgetShellEditorComponent {
868
- registry;
869
- form = new FormGroup({
870
- kind: new FormControl('dashboard-card', { nonNullable: true }),
871
- preset: new FormControl('', { nonNullable: true }),
872
- applyToAll: new FormControl(false, { nonNullable: true }),
873
- title: new FormControl('', { nonNullable: true }),
874
- subtitle: new FormControl('', { nonNullable: true }),
875
- icon: new FormControl('', { nonNullable: true }),
876
- showHeader: new FormControl('auto', { nonNullable: true }),
877
- collapsible: new FormControl(true, { nonNullable: true }),
878
- cardBg: new FormControl('', { nonNullable: true }),
879
- cardBorder: new FormControl('', { nonNullable: true }),
880
- cardRadius: new FormControl('', { nonNullable: true }),
881
- cardShadow: new FormControl('', { nonNullable: true }),
882
- headerBg: new FormControl('', { nonNullable: true }),
883
- headerBorder: new FormControl('', { nonNullable: true }),
884
- titleColor: new FormControl('', { nonNullable: true }),
885
- subtitleColor: new FormControl('', { nonNullable: true }),
886
- iconColor: new FormControl('', { nonNullable: true }),
887
- bodyBg: new FormControl('', { nonNullable: true }),
888
- bodyColor: new FormControl('', { nonNullable: true }),
889
- bodyPadding: new FormControl('', { nonNullable: true }),
890
- titleSize: new FormControl('', { nonNullable: true }),
891
- titleWeight: new FormControl('', { nonNullable: true }),
892
- subtitleSize: new FormControl('', { nonNullable: true }),
893
- actions: new FormArray([]),
894
- });
895
- initialShell = new BehaviorSubject(null);
896
- dirtySubject = new BehaviorSubject(false);
897
- validSubject = new BehaviorSubject(true);
898
- busySubject = new BehaviorSubject(false);
899
- isDirty$ = this.dirtySubject.asObservable();
900
- isValid$ = this.validSubject.asObservable();
901
- isBusy$ = this.busySubject.asObservable();
902
- snapshot = '';
903
- initialTitle;
904
- initialSubtitle;
905
- actionCopySources = new WeakMap();
906
- presetOptions = [...BUILTIN_PRESET_OPTIONS];
907
- presets = { ...BUILTIN_PRESET_MAP };
908
- availableActions = [];
909
- actionQuery = new FormControl('', { nonNullable: true });
910
- showScopeShell = true;
911
- showScopeAny = true;
912
- showScopeToolbar = false;
913
- showScopeContext = false;
914
- setInputPresets = [];
915
- setInputTarget = new FormControl('', { nonNullable: true });
916
- setInputValue = new FormControl(null);
917
- previewCollapsed = false;
918
- previewExpanded = false;
919
- pagePreset = null;
920
- constructor(data, registry) {
921
- this.registry = registry;
922
- const customPresets = data?.presets || {};
923
- const customOptions = Object.keys(customPresets).map((id) => ({ id, label: id }));
924
- this.presetOptions = [...BUILTIN_PRESET_OPTIONS, ...customOptions];
925
- this.presets = { ...BUILTIN_PRESET_MAP, ...customPresets };
926
- this.pagePreset = data?.pagePreset || null;
927
- const shell = data?.shell || null;
928
- this.initialShell.next(shell);
929
- this.applyShell(shell);
930
- this.loadAvailableActions(data?.widgetType || null);
931
- this.loadSetInputPresets(data?.widgetType || null);
932
- this.form.valueChanges
933
- .pipe(takeUntilDestroyed())
934
- .subscribe(() => {
935
- this.validSubject.next(this.form.valid);
936
- const current = this.snapshotValue();
937
- this.dirtySubject.next(current !== this.snapshot);
938
- });
939
- this.form.controls.preset.valueChanges
940
- .pipe(takeUntilDestroyed())
941
- .subscribe((value) => {
942
- if (!value && this.form.controls.applyToAll.value) {
943
- this.form.controls.applyToAll.setValue(false, { emitEvent: false });
944
- }
945
- });
946
- this.setInputTarget.valueChanges
947
- .pipe(takeUntilDestroyed())
948
- .subscribe(() => {
949
- const next = this.currentSetInputValues()[0];
950
- if (next)
951
- this.setInputValue.setValue(next.value, { emitEvent: false });
952
- });
953
- }
954
- get actions() {
955
- return this.form.controls.actions;
956
- }
957
- get hasAvailableActions() {
958
- return !!this.availableActions.length;
959
- }
960
- addAction(seed) {
961
- this.actions.push(this.createActionGroup(seed));
962
- }
963
- removeAction(index) {
964
- this.actions.removeAt(index);
965
- }
966
- getSettingsValue() {
967
- return this.buildResult();
968
- }
969
- onSave() {
970
- return this.buildResult();
971
- }
972
- reset() {
973
- const shell = this.initialShell.value;
974
- this.applyShell(shell);
975
- }
976
- applyShell(shell) {
977
- this.initialTitle = shell?.title;
978
- this.initialSubtitle = shell?.subtitle;
979
- const showHeader = shell?.showHeader == null ? 'auto' : shell.showHeader ? 'true' : 'false';
980
- const window = shell?.windowActions || {};
981
- const appearance = shell?.appearance || {};
982
- const card = appearance.card || {};
983
- const header = appearance.header || {};
984
- const body = appearance.body || {};
985
- const typography = appearance.typography || {};
986
- this.form.setControl('actions', new FormArray((shell?.actions || []).map((a) => this.createActionGroup(a))));
987
- this.form.setValue({
988
- kind: shell?.kind === 'none' ? 'none' : 'dashboard-card',
989
- preset: shell?.preset || '',
990
- applyToAll: false,
991
- title: this.editableText(shell?.title),
992
- subtitle: this.editableText(shell?.subtitle),
993
- icon: shell?.icon || '',
994
- showHeader,
995
- collapsible: window.collapsible !== false,
996
- cardBg: card.background || '',
997
- cardBorder: card.borderColor || '',
998
- cardRadius: card.borderRadius || '',
999
- cardShadow: card.shadow || '',
1000
- headerBg: header.background || '',
1001
- headerBorder: header.borderColor || '',
1002
- titleColor: header.titleColor || '',
1003
- subtitleColor: header.subtitleColor || '',
1004
- iconColor: header.iconColor || '',
1005
- bodyBg: body.background || '',
1006
- bodyColor: body.textColor || '',
1007
- bodyPadding: body.padding || '',
1008
- titleSize: typography.titleSize || '',
1009
- titleWeight: typography.titleWeight || '',
1010
- subtitleSize: typography.subtitleSize || '',
1011
- actions: this.actions.value,
1012
- }, { emitEvent: false });
1013
- this.snapshot = this.snapshotValue();
1014
- this.dirtySubject.next(false);
1015
- this.validSubject.next(this.form.valid);
1016
- }
1017
- loadAvailableActions(widgetType) {
1018
- if (!widgetType || !this.registry) {
1019
- this.availableActions = [];
1020
- return;
1021
- }
1022
- const meta = this.registry.get(widgetType);
1023
- const actions = (meta?.actions || []).map((action) => ({
1024
- kind: 'action',
1025
- id: action.id,
1026
- label: action.label || action.id,
1027
- icon: action.icon,
1028
- scope: action.scope || 'any',
1029
- emit: action.emit,
1030
- command: action.command,
1031
- description: action.description,
1032
- payloadExample: action.payloadSchema?.example,
1033
- }));
1034
- const commands = (meta?.commands || []).map((command) => ({
1035
- kind: 'command',
1036
- id: command.name,
1037
- label: command.label || command.name,
1038
- icon: command.icon,
1039
- scope: 'any',
1040
- command: command.name,
1041
- description: command.description,
1042
- payloadExample: command.payloadSchema?.example,
1043
- }));
1044
- this.availableActions = [...actions, ...commands];
1045
- }
1046
- createActionGroup(seed) {
1047
- const group = new FormGroup({
1048
- id: new FormControl(seed?.id || '', { nonNullable: true, validators: [Validators.required] }),
1049
- label: new FormControl(this.editableText(seed?.label), { nonNullable: true }),
1050
- icon: new FormControl(seed?.icon || '', { nonNullable: true }),
1051
- variant: new FormControl(seed?.variant || 'text', { nonNullable: true }),
1052
- placement: new FormControl(seed?.placement || 'header', { nonNullable: true }),
1053
- emit: new FormControl(seed?.emit || '', { nonNullable: true }),
1054
- tooltip: new FormControl(this.editableText(seed?.tooltip), { nonNullable: true }),
1055
- command: new FormControl(seed?.command || '', { nonNullable: true }),
1056
- payload: new FormControl(seed?.payload != null ? JSON.stringify(seed.payload) : '', { nonNullable: true }),
1057
- visible: new FormControl(seed?.visible !== false, { nonNullable: true }),
1058
- disabled: new FormControl(!!seed?.disabled, { nonNullable: true }),
1059
- });
1060
- this.actionCopySources.set(group, { label: seed?.label, tooltip: seed?.tooltip });
1061
- return group;
1062
- }
1063
- buildShell() {
1064
- const raw = this.form.getRawValue();
1065
- if (raw.kind === 'none') {
1066
- return { kind: 'none' };
1067
- }
1068
- const shell = {};
1069
- if (raw.preset)
1070
- shell.preset = raw.preset;
1071
- const title = this.preserveTextDescriptor(this.initialTitle, raw.title);
1072
- const subtitle = this.preserveTextDescriptor(this.initialSubtitle, raw.subtitle);
1073
- if (title)
1074
- shell.title = title;
1075
- if (subtitle)
1076
- shell.subtitle = subtitle;
1077
- if (raw.icon.trim())
1078
- shell.icon = raw.icon.trim();
1079
- if (raw.showHeader !== 'auto')
1080
- shell.showHeader = raw.showHeader === 'true';
1081
- const windowActions = {};
1082
- if (!raw.collapsible)
1083
- windowActions.collapsible = false;
1084
- if (Object.keys(windowActions).length)
1085
- shell.windowActions = windowActions;
1086
- const actions = this.actions.controls
1087
- .map((group) => this.normalizeAction(group.getRawValue(), this.actionCopySources.get(group)))
1088
- .filter((a) => a.id);
1089
- if (actions.length)
1090
- shell.actions = actions;
1091
- const appearance = this.buildAppearance(raw);
1092
- if (appearance)
1093
- shell.appearance = appearance;
1094
- return shell;
1095
- }
1096
- buildResult() {
1097
- const raw = this.form.getRawValue();
1098
- const shell = this.buildShell();
1099
- const applyToAll = !!raw.applyToAll && !!raw.preset;
1100
- return {
1101
- shell,
1102
- applyToAll,
1103
- pagePreset: applyToAll ? raw.preset : undefined,
1104
- };
1105
- }
1106
- buildAppearance(raw) {
1107
- const card = {};
1108
- const header = {};
1109
- const body = {};
1110
- const typography = {};
1111
- if (raw.cardBg)
1112
- card.background = raw.cardBg.trim();
1113
- if (raw.cardBorder)
1114
- card.borderColor = raw.cardBorder.trim();
1115
- if (raw.cardRadius)
1116
- card.borderRadius = raw.cardRadius.trim();
1117
- if (raw.cardShadow)
1118
- card.shadow = raw.cardShadow.trim();
1119
- if (raw.headerBg)
1120
- header.background = raw.headerBg.trim();
1121
- if (raw.headerBorder)
1122
- header.borderColor = raw.headerBorder.trim();
1123
- if (raw.titleColor)
1124
- header.titleColor = raw.titleColor.trim();
1125
- if (raw.subtitleColor)
1126
- header.subtitleColor = raw.subtitleColor.trim();
1127
- if (raw.iconColor)
1128
- header.iconColor = raw.iconColor.trim();
1129
- if (raw.bodyBg)
1130
- body.background = raw.bodyBg.trim();
1131
- if (raw.bodyColor)
1132
- body.textColor = raw.bodyColor.trim();
1133
- if (raw.bodyPadding)
1134
- body.padding = raw.bodyPadding.trim();
1135
- if (raw.titleSize)
1136
- typography.titleSize = raw.titleSize.trim();
1137
- if (raw.titleWeight)
1138
- typography.titleWeight = raw.titleWeight.trim();
1139
- if (raw.subtitleSize)
1140
- typography.subtitleSize = raw.subtitleSize.trim();
1141
- const appearance = {};
1142
- if (Object.keys(card).length)
1143
- appearance.card = card;
1144
- if (Object.keys(header).length)
1145
- appearance.header = header;
1146
- if (Object.keys(body).length)
1147
- appearance.body = body;
1148
- if (Object.keys(typography).length)
1149
- appearance.typography = typography;
1150
- return Object.keys(appearance).length ? appearance : undefined;
1151
- }
1152
- previewVars() {
1153
- const appearance = this.buildPreviewAppearance();
1154
- return {
1155
- '--pdx-shell-card-bg': appearance?.card?.background || null,
1156
- '--pdx-shell-card-border': appearance?.card?.borderColor || null,
1157
- '--pdx-shell-card-radius': appearance?.card?.borderRadius || null,
1158
- '--pdx-shell-card-shadow': appearance?.card?.shadow || null,
1159
- '--pdx-shell-header-bg': appearance?.header?.background || null,
1160
- '--pdx-shell-header-border': appearance?.header?.borderColor || null,
1161
- '--pdx-shell-title-color': appearance?.header?.titleColor || null,
1162
- '--pdx-shell-subtitle-color': appearance?.header?.subtitleColor || null,
1163
- '--pdx-shell-icon-color': appearance?.header?.iconColor || null,
1164
- '--pdx-shell-body-bg': appearance?.body?.background || null,
1165
- '--pdx-shell-body-color': appearance?.body?.textColor || null,
1166
- '--pdx-shell-body-padding': appearance?.body?.padding || null,
1167
- '--pdx-shell-title-size': appearance?.typography?.titleSize || null,
1168
- '--pdx-shell-title-weight': appearance?.typography?.titleWeight || null,
1169
- '--pdx-shell-subtitle-size': appearance?.typography?.subtitleSize || null,
1170
- };
1171
- }
1172
- previewShowHeader() {
1173
- const raw = this.form.getRawValue();
1174
- if (raw.kind === 'none')
1175
- return false;
1176
- if (raw.showHeader === 'true')
1177
- return true;
1178
- if (raw.showHeader === 'false')
1179
- return false;
1180
- return !!(raw.title || raw.subtitle || raw.icon);
1181
- }
1182
- previewTitle() {
1183
- const title = this.editableText(this.form.controls.title.value).trim();
1184
- return title || 'Titulo do card';
1185
- }
1186
- previewSubtitle() {
1187
- const subtitle = this.editableText(this.form.controls.subtitle.value).trim();
1188
- return subtitle || 'Subtitulo opcional';
1189
- }
1190
- buildPreviewAppearance() {
1191
- const raw = this.form.getRawValue();
1192
- const presetId = raw.preset || this.pagePreset || '';
1193
- const base = presetId ? this.presets[presetId] : undefined;
1194
- const override = this.buildAppearance(raw);
1195
- return this.mergeAppearance(base, override);
1196
- }
1197
- mergeAppearance(base, override) {
1198
- if (!base && !override)
1199
- return undefined;
1200
- const out = {};
1201
- const mergeSection = (key) => {
1202
- const a = (base && base[key]) || undefined;
1203
- const b = (override && override[key]) || undefined;
1204
- if (!a && !b)
1205
- return;
1206
- out[key] = { ...(a || {}), ...(b || {}) };
1207
- };
1208
- mergeSection('card');
1209
- mergeSection('header');
1210
- mergeSection('body');
1211
- mergeSection('typography');
1212
- return out;
1213
- }
1214
- togglePreviewCollapse() {
1215
- this.previewCollapsed = !this.previewCollapsed;
1216
- }
1217
- togglePreviewExpand() {
1218
- this.previewExpanded = !this.previewExpanded;
1219
- }
1220
- previewHeaderActions() {
1221
- const actions = this.previewActions().filter((a) => (a.placement || 'header') === 'header');
1222
- return actions.slice(0, 3);
1223
- }
1224
- previewOverflowCount() {
1225
- const actions = this.previewActions().filter((a) => (a.placement || 'header') === 'header');
1226
- return Math.max(0, actions.length - 3);
1227
- }
1228
- previewWindowActions() {
1229
- return this.previewActions().filter((a) => (a.placement || 'header') === 'window');
1230
- }
1231
- normalizeAction(raw, source) {
1232
- const action = { id: String(raw.id || '').trim() };
1233
- const label = String(raw.label || '').trim();
1234
- const icon = String(raw.icon || '').trim();
1235
- const emit = String(raw.emit || '').trim();
1236
- const tooltip = String(raw.tooltip || '').trim();
1237
- const command = String(raw.command || '').trim();
1238
- const normalizedLabel = this.preserveTextDescriptor(source?.label, label);
1239
- const normalizedTooltip = this.preserveTextDescriptor(source?.tooltip, tooltip);
1240
- if (normalizedLabel)
1241
- action.label = normalizedLabel;
1242
- if (icon)
1243
- action.icon = icon;
1244
- if (emit)
1245
- action.emit = emit;
1246
- if (normalizedTooltip)
1247
- action.tooltip = normalizedTooltip;
1248
- if (command)
1249
- action.command = command;
1250
- if (raw.variant && raw.variant !== 'text')
1251
- action.variant = raw.variant;
1252
- if (raw.placement && raw.placement !== 'header')
1253
- action.placement = raw.placement;
1254
- if (raw.visible === false)
1255
- action.visible = false;
1256
- if (raw.disabled === true)
1257
- action.disabled = true;
1258
- const payload = this.tryParsePayload(raw.payload);
1259
- if (payload !== undefined)
1260
- action.payload = payload;
1261
- return action;
1262
- }
1263
- editableText(value) {
1264
- if (typeof value === 'string')
1265
- return value;
1266
- return value?.text || value?.key || '';
1267
- }
1268
- preserveTextDescriptor(source, editedValue) {
1269
- const edited = String(editedValue ?? '').trim();
1270
- if (!edited)
1271
- return undefined;
1272
- if (source && typeof source === 'object') {
1273
- const authoredText = this.editableText(source).trim();
1274
- return edited === authoredText ? { ...source } : edited;
1275
- }
1276
- return edited;
1277
- }
1278
- tryParsePayload(raw) {
1279
- const value = String(raw || '').trim();
1280
- if (!value)
1281
- return undefined;
1282
- try {
1283
- return JSON.parse(value);
1284
- }
1285
- catch {
1286
- return value;
1287
- }
1288
- }
1289
- snapshotValue() {
1290
- try {
1291
- return JSON.stringify(this.buildResult());
1292
- }
1293
- catch {
1294
- return '';
1295
- }
1296
- }
1297
- filteredAvailableActions() {
1298
- const query = this.actionQuery.value.trim().toLowerCase();
1299
- const scopes = new Set();
1300
- if (this.showScopeShell)
1301
- scopes.add('shell');
1302
- if (this.showScopeAny)
1303
- scopes.add('any');
1304
- if (this.showScopeToolbar)
1305
- scopes.add('toolbar');
1306
- if (this.showScopeContext)
1307
- scopes.add('context');
1308
- return this.availableActions.filter((action) => {
1309
- const scope = action.scope || 'any';
1310
- if (!scopes.has(scope))
1311
- return false;
1312
- if (!query)
1313
- return true;
1314
- const hay = [
1315
- action.label,
1316
- action.id,
1317
- action.description,
1318
- action.emit,
1319
- action.command,
1320
- ]
1321
- .filter(Boolean)
1322
- .join(' ')
1323
- .toLowerCase();
1324
- return hay.includes(query);
1325
- });
1326
- }
1327
- isActionSelected(action) {
1328
- return this.findActionIndex(action) >= 0;
1329
- }
1330
- toggleAvailableAction(action) {
1331
- const idx = this.findActionIndex(action);
1332
- if (idx >= 0) {
1333
- this.removeAction(idx);
1334
- return;
1335
- }
1336
- this.addAction({
1337
- id: action.id,
1338
- label: action.label,
1339
- icon: action.icon,
1340
- emit: action.emit,
1341
- command: action.command,
1342
- payload: action.payloadExample,
1343
- });
1344
- }
1345
- scopeLabel(scope) {
1346
- const value = scope || 'any';
1347
- if (value === 'shell')
1348
- return 'Shell';
1349
- if (value === 'toolbar')
1350
- return 'Toolbar';
1351
- if (value === 'context')
1352
- return 'Context';
1353
- return 'Any';
1354
- }
1355
- stringifyPayload(payload) {
1356
- if (payload === null)
1357
- return 'null';
1358
- if (payload === undefined)
1359
- return 'undefined';
1360
- if (typeof payload === 'string')
1361
- return payload;
1362
- try {
1363
- return JSON.stringify(payload);
1364
- }
1365
- catch {
1366
- return String(payload);
1367
- }
1368
- }
1369
- displayInputPath(path) {
1370
- const trimmed = String(path || '').trim();
1371
- if (!trimmed)
1372
- return '';
1373
- return trimmed.replace(/^inputs\./, '');
1374
- }
1375
- extractActionInput(actionValue) {
1376
- const payload = this.tryParsePayload(actionValue?.payload);
1377
- const input = typeof payload?.input === 'string' ? payload.input.trim() : '';
1378
- return input;
1379
- }
1380
- showActionInputPath(actionValue) {
1381
- const command = String(actionValue?.command || '').trim();
1382
- if (command !== 'pdx:set-input')
1383
- return false;
1384
- return !!this.extractActionInput(actionValue);
1385
- }
1386
- currentSetInputValues() {
1387
- const preset = this.setInputPresets.find((p) => p.input === this.setInputTarget.value);
1388
- return preset?.values || [];
1389
- }
1390
- addSetInputAction() {
1391
- const preset = this.setInputPresets.find((p) => p.input === this.setInputTarget.value);
1392
- if (!preset)
1393
- return;
1394
- const selected = this.currentSetInputValues().find((v) => v.value === this.setInputValue.value);
1395
- if (!selected)
1396
- return;
1397
- const label = `${preset.label}: ${selected.label}`;
1398
- const payload = { input: preset.input, value: selected.value };
1399
- this.addAction({
1400
- id: `set-${preset.input}-${String(selected.value)}`,
1401
- label,
1402
- icon: selected.icon || preset.icon,
1403
- command: 'pdx:set-input',
1404
- payload,
1405
- variant: 'text',
1406
- placement: 'header',
1407
- });
1408
- }
1409
- findActionIndex(action) {
1410
- const values = this.actions.getRawValue();
1411
- return values.findIndex((a) => {
1412
- if (a.id !== action.id)
1413
- return false;
1414
- if (action.command && a.command)
1415
- return a.command === action.command;
1416
- if (action.emit && a.emit)
1417
- return a.emit === action.emit;
1418
- return true;
1419
- });
1420
- }
1421
- previewActions() {
1422
- return (this.actions.getRawValue() || [])
1423
- .map((a) => this.normalizeAction(a))
1424
- .filter((a) => a.id && a.visible !== false);
1425
- }
1426
- loadSetInputPresets(widgetType) {
1427
- if (!widgetType) {
1428
- this.setInputPresets = [];
1429
- return;
1430
- }
1431
- this.setInputPresets = SET_INPUT_PRESETS[widgetType] || [];
1432
- const first = this.setInputPresets[0];
1433
- if (first) {
1434
- this.setInputTarget.setValue(first.input, { emitEvent: false });
1435
- const firstValue = first.values[0];
1436
- if (firstValue)
1437
- this.setInputValue.setValue(firstValue.value, { emitEvent: false });
1438
- }
1439
- }
1440
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: WidgetShellEditorComponent, deps: [{ token: SETTINGS_PANEL_DATA, optional: true }, { token: i2$2.ComponentMetadataRegistry, optional: true }], target: i0.ɵɵFactoryTarget.Component });
1441
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: WidgetShellEditorComponent, isStandalone: true, selector: "praxis-widget-shell-editor", ngImport: i0, template: `
1442
- <div class="shell-editor">
1443
- <p class="shell-intro">
1444
- Defina a identidade, a aparência e as ações do card. Use a prévia para conferir o resultado antes de salvar.
1445
- </p>
1446
-
1447
- <div class="shell-section">Geral</div>
1448
- <div class="shell-grid">
1449
- <mat-form-field appearance="outline">
1450
- <mat-label>Tipo de card</mat-label>
1451
- <mat-select [formControl]="form.controls.kind">
1452
- <mat-option value="dashboard-card">Dashboard Card</mat-option>
1453
- <mat-option value="none">Sem shell</mat-option>
1454
- </mat-select>
1455
- </mat-form-field>
1456
- <mat-form-field appearance="outline">
1457
- <mat-label>Preset</mat-label>
1458
- <mat-select [formControl]="form.controls.preset">
1459
- <mat-option value="">Nenhum</mat-option>
1460
- @if (pagePreset) {
1461
- <mat-option [value]="pagePreset">Usar preset da página ({{ pagePreset }})</mat-option>
1462
- }
1463
- @for (p of presetOptions; track p) {
1464
- <mat-option [value]="p.id">{{ p.label }}</mat-option>
1465
- }
1466
- </mat-select>
1467
- </mat-form-field>
1468
- @if (!form.controls.preset.value && pagePreset) {
1469
- <div class="shell-hint">
1470
- Preset da página: {{ pagePreset }}
1471
- </div>
1472
- }
1473
- @if (form.controls.preset.value) {
1474
- <mat-checkbox [formControl]="form.controls.applyToAll">
1475
- Aplicar preset como padrão da página
1476
- </mat-checkbox>
1477
- }
1478
- <mat-form-field appearance="outline">
1479
- <mat-label>Ícone</mat-label>
1480
- <input matInput [formControl]="form.controls.icon" placeholder="ex: badge" />
1481
- </mat-form-field>
1482
- <mat-form-field appearance="outline">
1483
- <mat-label>Título</mat-label>
1484
- <input matInput [formControl]="form.controls.title" />
1485
- </mat-form-field>
1486
- <mat-form-field appearance="outline" class="shell-field--wide">
1487
- <mat-label>Subtítulo</mat-label>
1488
- <input matInput [formControl]="form.controls.subtitle" />
1489
- </mat-form-field>
1490
- <mat-form-field appearance="outline">
1491
- <mat-label>Mostrar cabeçalho</mat-label>
1492
- <mat-select [formControl]="form.controls.showHeader">
1493
- <mat-option value="auto">Automático</mat-option>
1494
- <mat-option value="true">Sempre</mat-option>
1495
- <mat-option value="false">Nunca</mat-option>
1496
- </mat-select>
1497
- </mat-form-field>
1498
- </div>
1499
-
1500
- <div class="shell-section">Controles de janela</div>
1501
- <div class="shell-flags">
1502
- <mat-checkbox [formControl]="form.controls.collapsible">Permitir recolher</mat-checkbox>
1503
- </div>
1504
-
1505
- <mat-divider></mat-divider>
1506
-
1507
- <div class="shell-section">Aparência</div>
1508
- <div class="shell-grid">
1509
- <mat-form-field appearance="outline">
1510
- <mat-label>Fundo do card</mat-label>
1511
- <input matInput [formControl]="form.controls.cardBg" placeholder="ex: #0b1220" />
1512
- </mat-form-field>
1513
- <mat-form-field appearance="outline">
1514
- <mat-label>Cor da borda</mat-label>
1515
- <input matInput [formControl]="form.controls.cardBorder" placeholder="ex: rgba(255,255,255,0.12)" />
1516
- </mat-form-field>
1517
- <mat-form-field appearance="outline">
1518
- <mat-label>Raio da borda</mat-label>
1519
- <input matInput [formControl]="form.controls.cardRadius" placeholder="ex: 14px" />
1520
- </mat-form-field>
1521
- <mat-form-field appearance="outline">
1522
- <mat-label>Sombra</mat-label>
1523
- <input matInput [formControl]="form.controls.cardShadow" placeholder="ex: 0 10px 30px rgba(0,0,0,0.2)" />
1524
- </mat-form-field>
1525
- <mat-form-field appearance="outline">
1526
- <mat-label>Fundo do cabeçalho</mat-label>
1527
- <input matInput [formControl]="form.controls.headerBg" placeholder="ex: linear-gradient(...)" />
1528
- </mat-form-field>
1529
- <mat-form-field appearance="outline">
1530
- <mat-label>Borda do cabeçalho</mat-label>
1531
- <input matInput [formControl]="form.controls.headerBorder" placeholder="ex: rgba(255,255,255,0.08)" />
1532
- </mat-form-field>
1533
- <mat-form-field appearance="outline">
1534
- <mat-label>Cor do título</mat-label>
1535
- <input matInput [formControl]="form.controls.titleColor" placeholder="ex: #e6edf3" />
1536
- </mat-form-field>
1537
- <mat-form-field appearance="outline">
1538
- <mat-label>Cor do subtítulo</mat-label>
1539
- <input matInput [formControl]="form.controls.subtitleColor" placeholder="ex: #9fb0c4" />
1540
- </mat-form-field>
1541
- <mat-form-field appearance="outline">
1542
- <mat-label>Cor do ícone</mat-label>
1543
- <input matInput [formControl]="form.controls.iconColor" placeholder="ex: #8ab4ff" />
1544
- </mat-form-field>
1545
- <mat-form-field appearance="outline">
1546
- <mat-label>Fundo do corpo</mat-label>
1547
- <input matInput [formControl]="form.controls.bodyBg" placeholder="ex: transparent" />
1548
- </mat-form-field>
1549
- <mat-form-field appearance="outline">
1550
- <mat-label>Cor do texto do corpo</mat-label>
1551
- <input matInput [formControl]="form.controls.bodyColor" placeholder="ex: #e6edf3" />
1552
- </mat-form-field>
1553
- <mat-form-field appearance="outline">
1554
- <mat-label>Padding do corpo</mat-label>
1555
- <input matInput [formControl]="form.controls.bodyPadding" placeholder="ex: 10px 12px 12px" />
1556
- </mat-form-field>
1557
- <mat-form-field appearance="outline">
1558
- <mat-label>Tamanho do título</mat-label>
1559
- <input matInput [formControl]="form.controls.titleSize" placeholder="ex: 14px" />
1560
- </mat-form-field>
1561
- <mat-form-field appearance="outline">
1562
- <mat-label>Peso do título</mat-label>
1563
- <input matInput [formControl]="form.controls.titleWeight" placeholder="ex: 600" />
1564
- </mat-form-field>
1565
- <mat-form-field appearance="outline">
1566
- <mat-label>Tamanho do subtítulo</mat-label>
1567
- <input matInput [formControl]="form.controls.subtitleSize" placeholder="ex: 12px" />
1568
- </mat-form-field>
1569
- </div>
1570
-
1571
- <div class="shell-preview" [ngStyle]="previewVars()">
1572
- <div class="shell-preview-card" [class.no-shell]="form.controls.kind.value === 'none'" [class.expanded]="previewExpanded">
1573
- @if (previewShowHeader()) {
1574
- <div class="shell-preview-header">
1575
- <div class="shell-preview-title">
1576
- @if (form.controls.icon.value) {
1577
- <mat-icon [praxisIcon]="form.controls.icon.value"></mat-icon>
1578
- }
1579
- <div class="shell-preview-text">
1580
- <div class="shell-preview-title-text">{{ previewTitle() }}</div>
1581
- <div class="shell-preview-subtitle">{{ previewSubtitle() }}</div>
1582
- </div>
1583
- </div>
1584
- <div class="shell-preview-actions">
1585
- @for (action of previewHeaderActions(); track action.id) {
1586
- <button
1587
- type="button"
1588
- [disabled]="action.disabled"
1589
- [ngClass]="action.variant === 'outlined' ? 'pdx-preview-outlined' : 'pdx-preview-text'"
1590
- mat-button
1591
- >
1592
- @if (action.icon) {
1593
- <mat-icon [praxisIcon]="action.icon"></mat-icon>
1594
- }
1595
- <span class="pdx-preview-label">{{ action.label || action.id }}</span>
1596
- </button>
1597
- }
1598
- @if (previewOverflowCount()) {
1599
- <button
1600
- praxisIconButton="more_horiz"
1601
- size="compact"
1602
- type="button"
1603
- aria-label="Mais ações"
1604
- ></button>
1605
- }
1606
- @for (action of previewWindowActions(); track action.id) {
1607
- @if (action.icon) {
1608
- <button
1609
- [praxisIconButton]="action.icon"
1610
- size="compact"
1611
- type="button"
1612
- [disabled]="action.disabled"
1613
- [attr.aria-label]="action.label || action.id"
1614
- ></button>
1615
- }
1616
- }
1617
- <button
1618
- [praxisIconButton]="previewCollapsed ? 'unfold_more' : 'unfold_less'"
1619
- size="compact"
1620
- type="button"
1621
- (click)="togglePreviewCollapse()"
1622
- aria-label="Recolher"
1623
- ></button>
1624
- <button
1625
- [praxisIconButton]="previewExpanded ? 'close_fullscreen' : 'open_in_full'"
1626
- size="compact"
1627
- type="button"
1628
- (click)="togglePreviewExpand()"
1629
- aria-label="Expandir"
1630
- ></button>
1631
- </div>
1632
- </div>
1633
- }
1634
- <div class="shell-preview-body" [class.hidden]="previewCollapsed">Conteudo do widget</div>
1635
- </div>
1636
- </div>
1637
-
1638
- <mat-divider></mat-divider>
1639
-
1640
- @if (hasAvailableActions) {
1641
- <div class="shell-section">Ações disponíveis</div>
1642
- }
1643
- @if (hasAvailableActions) {
1644
- <div class="shell-available">
1645
- <div class="shell-available-toolbar">
1646
- <mat-form-field appearance="outline">
1647
- <mat-label>Buscar ação</mat-label>
1648
- <input matInput [formControl]="actionQuery" placeholder="ex: export, editar, submit" />
1649
- </mat-form-field>
1650
- <div class="shell-available-filters">
1651
- <mat-checkbox [(ngModel)]="showScopeShell">Shell</mat-checkbox>
1652
- <mat-checkbox [(ngModel)]="showScopeAny">Any</mat-checkbox>
1653
- <mat-checkbox [(ngModel)]="showScopeToolbar">Toolbar</mat-checkbox>
1654
- <mat-checkbox [(ngModel)]="showScopeContext">Context</mat-checkbox>
1655
- </div>
1656
- </div>
1657
- <div class="shell-available-list">
1658
- @for (action of filteredAvailableActions(); track action) {
1659
- <div class="shell-available-item">
1660
- <div class="shell-available-main">
1661
- <div class="shell-available-title">
1662
- @if (action.icon) {
1663
- <mat-icon [praxisIcon]="action.icon"></mat-icon>
1664
- }
1665
- <div class="shell-available-text">
1666
- <div class="shell-available-label">{{ action.label }}</div>
1667
- <div class="shell-available-meta">
1668
- <span class="shell-badge">{{ action.kind === 'command' ? 'Comando' : 'Ação' }}</span>
1669
- <span class="shell-badge">{{ scopeLabel(action.scope) }}</span>
1670
- <span class="shell-code">id: {{ action.id }}</span>
1671
- @if (action.emit) {
1672
- <span class="shell-code">emit: {{ action.emit }}</span>
1673
- }
1674
- @if (action.command) {
1675
- <span class="shell-code">command: {{ action.command }}</span>
1676
- }
1677
- </div>
1678
- </div>
1679
- </div>
1680
- @if (action.description) {
1681
- <div class="shell-available-desc">{{ action.description }}</div>
1682
- }
1683
- @if (action.payloadExample !== undefined) {
1684
- <div class="shell-available-payload">
1685
- <span class="shell-code">payload: {{ stringifyPayload(action.payloadExample) }}</span>
1686
- </div>
1687
- }
1688
- @if (action.scope === 'context') {
1689
- <div class="shell-available-warning">
1690
- Essa ação depende de item/linha selecionado; payload fixo pode não funcionar.
1691
- </div>
1692
- }
1693
- </div>
1694
- <div class="shell-available-actions">
1695
- <button
1696
- mat-stroked-button
1697
- type="button"
1698
- [color]="isActionSelected(action) ? 'warn' : 'primary'"
1699
- (click)="toggleAvailableAction(action)"
1700
- >
1701
- {{ isActionSelected(action) ? 'Remover' : 'Adicionar' }}
1702
- </button>
1703
- </div>
1704
- </div>
1705
- }
1706
- </div>
1707
- </div>
1708
- } @else {
1709
- <div class="shell-empty">Nenhuma ação registrada para este componente.</div>
1710
- }
1711
-
1712
- @if (hasAvailableActions) {
1713
- <mat-divider></mat-divider>
1714
- }
1715
-
1716
- @if (setInputPresets.length) {
1717
- <div class="shell-section">Ações rápidas (inputs)</div>
1718
- }
1719
- @if (setInputPresets.length) {
1720
- <div class="shell-quick-inputs">
1721
- <mat-form-field appearance="outline">
1722
- <mat-label>Input</mat-label>
1723
- <mat-select [formControl]="setInputTarget">
1724
- @for (preset of setInputPresets; track preset) {
1725
- <mat-option [value]="preset.input">
1726
- @if (preset.icon) {
1727
- <mat-icon [praxisIcon]="preset.icon"></mat-icon>
1728
- }
1729
- {{ preset.label }}
1730
- </mat-option>
1731
- }
1732
- </mat-select>
1733
- </mat-form-field>
1734
- @if (setInputTarget.value) {
1735
- <div class="shell-input-path">
1736
- Input: {{ displayInputPath(setInputTarget.value) }}
1737
- </div>
1738
- }
1739
- <mat-form-field appearance="outline">
1740
- <mat-label>Valor</mat-label>
1741
- <mat-select [formControl]="setInputValue">
1742
- @for (option of currentSetInputValues(); track option) {
1743
- <mat-option [value]="option.value">
1744
- @if (option.icon) {
1745
- <mat-icon [praxisIcon]="option.icon"></mat-icon>
1746
- }
1747
- {{ option.label }}
1748
- </mat-option>
1749
- }
1750
- </mat-select>
1751
- </mat-form-field>
1752
- <button mat-flat-button color="primary" type="button" (click)="addSetInputAction()">
1753
- Adicionar ação
1754
- </button>
1755
- </div>
1756
- }
1757
-
1758
- <div class="shell-section shell-actions-head">
1759
- <div>Ações do cabeçalho</div>
1760
- <button mat-flat-button color="primary" type="button" (click)="addAction()">Adicionar ação</button>
1761
- </div>
1762
-
1763
- @if (actions.controls.length) {
1764
- <div class="shell-actions">
1765
- @for (action of actions.controls; track action; let i = $index) {
1766
- <div class="shell-action" [formGroup]="action">
1767
- <div class="shell-action-row">
1768
- <mat-form-field appearance="outline">
1769
- <mat-label>ID</mat-label>
1770
- <input matInput formControlName="id" />
1771
- </mat-form-field>
1772
- <mat-form-field appearance="outline">
1773
- <mat-label>Label</mat-label>
1774
- <input matInput formControlName="label" />
1775
- </mat-form-field>
1776
- <mat-form-field appearance="outline">
1777
- <mat-label>Ícone</mat-label>
1778
- <input matInput formControlName="icon" />
1779
- </mat-form-field>
1780
- </div>
1781
- <div class="shell-action-row">
1782
- <mat-form-field appearance="outline">
1783
- <mat-label>Variante</mat-label>
1784
- <mat-select formControlName="variant">
1785
- <mat-option value="text">Texto</mat-option>
1786
- <mat-option value="outlined">Outlined</mat-option>
1787
- <mat-option value="icon">Ícone</mat-option>
1788
- </mat-select>
1789
- </mat-form-field>
1790
- <mat-form-field appearance="outline">
1791
- <mat-label>Posição</mat-label>
1792
- <mat-select formControlName="placement">
1793
- <mat-option value="header">Cabeçalho</mat-option>
1794
- <mat-option value="window">Janela</mat-option>
1795
- </mat-select>
1796
- </mat-form-field>
1797
- <mat-form-field appearance="outline">
1798
- <mat-label>Emit</mat-label>
1799
- <input matInput formControlName="emit" placeholder="ex: editar" />
1800
- </mat-form-field>
1801
- <mat-form-field appearance="outline">
1802
- <mat-label>Tooltip</mat-label>
1803
- <input matInput formControlName="tooltip" />
1804
- </mat-form-field>
1805
- </div>
1806
- <div class="shell-action-row">
1807
- <mat-form-field appearance="outline">
1808
- <mat-label>Command</mat-label>
1809
- <input matInput formControlName="command" placeholder="ex: add-item" />
1810
- </mat-form-field>
1811
- <mat-form-field appearance="outline">
1812
- <mat-label>Payload (JSON)</mat-label>
1813
- <textarea matInput rows="2" formControlName="payload" placeholder='ex: {"foo":"bar"}'></textarea>
1814
- </mat-form-field>
1815
- <mat-checkbox formControlName="visible">Visível</mat-checkbox>
1816
- <mat-checkbox formControlName="disabled">Desabilitado</mat-checkbox>
1817
- </div>
1818
- @if (showActionInputPath(action.value)) {
1819
- <div class="shell-action-hint">
1820
- Input: {{ displayInputPath(extractActionInput(action.value)) }}
1821
- </div>
1822
- }
1823
- <div class="shell-action-actions">
1824
- <button mat-stroked-button color="warn" type="button" (click)="removeAction(i)">Remover</button>
1825
- </div>
1826
- </div>
1827
- }
1828
- </div>
1829
- } @else {
1830
- <div class="shell-empty">Nenhuma ação configurada.</div>
1831
- }
1832
-
1833
- </div>
1834
- `, isInline: true, styles: [".shell-editor{display:grid;gap:20px;padding:12px 4px 20px}.shell-intro{margin:0;max-width:68ch;color:var(--md-sys-color-on-surface-variant);font-size:14px;line-height:1.5}.shell-section{margin:4px 0 -8px;color:var(--md-sys-color-on-surface);font-size:14px;font-weight:650;line-height:1.4}.shell-grid{display:grid;gap:12px 16px;grid-template-columns:repeat(2,minmax(0,1fr));align-items:start}.shell-field--wide{grid-column:1 / -1}.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}@media(max-width:640px){.shell-editor{padding-inline:0}.shell-grid{grid-template-columns:minmax(0,1fr)}.shell-field--wide{grid-column:auto}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.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: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i5$1.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: i6.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.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$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1835
- }
1836
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: WidgetShellEditorComponent, decorators: [{
1837
- type: Component,
1838
- args: [{ selector: 'praxis-widget-shell-editor', standalone: true, imports: [
1839
- CommonModule,
1840
- FormsModule,
1841
- ReactiveFormsModule,
1842
- MatButtonModule,
1843
- MatCheckboxModule,
1844
- MatDividerModule,
1845
- MatFormFieldModule,
1846
- MatIconModule,
1847
- MatInputModule,
1848
- MatSelectModule,
1849
- PraxisIconButtonComponent,
1850
- PraxisIconDirective,
1851
- ], template: `
1852
- <div class="shell-editor">
1853
- <p class="shell-intro">
1854
- Defina a identidade, a aparência e as ações do card. Use a prévia para conferir o resultado antes de salvar.
1855
- </p>
1856
-
1857
- <div class="shell-section">Geral</div>
1858
- <div class="shell-grid">
1859
- <mat-form-field appearance="outline">
1860
- <mat-label>Tipo de card</mat-label>
1861
- <mat-select [formControl]="form.controls.kind">
1862
- <mat-option value="dashboard-card">Dashboard Card</mat-option>
1863
- <mat-option value="none">Sem shell</mat-option>
1864
- </mat-select>
1865
- </mat-form-field>
1866
- <mat-form-field appearance="outline">
1867
- <mat-label>Preset</mat-label>
1868
- <mat-select [formControl]="form.controls.preset">
1869
- <mat-option value="">Nenhum</mat-option>
1870
- @if (pagePreset) {
1871
- <mat-option [value]="pagePreset">Usar preset da página ({{ pagePreset }})</mat-option>
1872
- }
1873
- @for (p of presetOptions; track p) {
1874
- <mat-option [value]="p.id">{{ p.label }}</mat-option>
1875
- }
1876
- </mat-select>
1877
- </mat-form-field>
1878
- @if (!form.controls.preset.value && pagePreset) {
1879
- <div class="shell-hint">
1880
- Preset da página: {{ pagePreset }}
1881
- </div>
1882
- }
1883
- @if (form.controls.preset.value) {
1884
- <mat-checkbox [formControl]="form.controls.applyToAll">
1885
- Aplicar preset como padrão da página
1886
- </mat-checkbox>
1887
- }
1888
- <mat-form-field appearance="outline">
1889
- <mat-label>Ícone</mat-label>
1890
- <input matInput [formControl]="form.controls.icon" placeholder="ex: badge" />
1891
- </mat-form-field>
1892
- <mat-form-field appearance="outline">
1893
- <mat-label>Título</mat-label>
1894
- <input matInput [formControl]="form.controls.title" />
1895
- </mat-form-field>
1896
- <mat-form-field appearance="outline" class="shell-field--wide">
1897
- <mat-label>Subtítulo</mat-label>
1898
- <input matInput [formControl]="form.controls.subtitle" />
1899
- </mat-form-field>
1900
- <mat-form-field appearance="outline">
1901
- <mat-label>Mostrar cabeçalho</mat-label>
1902
- <mat-select [formControl]="form.controls.showHeader">
1903
- <mat-option value="auto">Automático</mat-option>
1904
- <mat-option value="true">Sempre</mat-option>
1905
- <mat-option value="false">Nunca</mat-option>
1906
- </mat-select>
1907
- </mat-form-field>
1908
- </div>
1909
-
1910
- <div class="shell-section">Controles de janela</div>
1911
- <div class="shell-flags">
1912
- <mat-checkbox [formControl]="form.controls.collapsible">Permitir recolher</mat-checkbox>
1913
- </div>
1914
-
1915
- <mat-divider></mat-divider>
1916
-
1917
- <div class="shell-section">Aparência</div>
1918
- <div class="shell-grid">
1919
- <mat-form-field appearance="outline">
1920
- <mat-label>Fundo do card</mat-label>
1921
- <input matInput [formControl]="form.controls.cardBg" placeholder="ex: #0b1220" />
1922
- </mat-form-field>
1923
- <mat-form-field appearance="outline">
1924
- <mat-label>Cor da borda</mat-label>
1925
- <input matInput [formControl]="form.controls.cardBorder" placeholder="ex: rgba(255,255,255,0.12)" />
1926
- </mat-form-field>
1927
- <mat-form-field appearance="outline">
1928
- <mat-label>Raio da borda</mat-label>
1929
- <input matInput [formControl]="form.controls.cardRadius" placeholder="ex: 14px" />
1930
- </mat-form-field>
1931
- <mat-form-field appearance="outline">
1932
- <mat-label>Sombra</mat-label>
1933
- <input matInput [formControl]="form.controls.cardShadow" placeholder="ex: 0 10px 30px rgba(0,0,0,0.2)" />
1934
- </mat-form-field>
1935
- <mat-form-field appearance="outline">
1936
- <mat-label>Fundo do cabeçalho</mat-label>
1937
- <input matInput [formControl]="form.controls.headerBg" placeholder="ex: linear-gradient(...)" />
1938
- </mat-form-field>
1939
- <mat-form-field appearance="outline">
1940
- <mat-label>Borda do cabeçalho</mat-label>
1941
- <input matInput [formControl]="form.controls.headerBorder" placeholder="ex: rgba(255,255,255,0.08)" />
1942
- </mat-form-field>
1943
- <mat-form-field appearance="outline">
1944
- <mat-label>Cor do título</mat-label>
1945
- <input matInput [formControl]="form.controls.titleColor" placeholder="ex: #e6edf3" />
1946
- </mat-form-field>
1947
- <mat-form-field appearance="outline">
1948
- <mat-label>Cor do subtítulo</mat-label>
1949
- <input matInput [formControl]="form.controls.subtitleColor" placeholder="ex: #9fb0c4" />
1950
- </mat-form-field>
1951
- <mat-form-field appearance="outline">
1952
- <mat-label>Cor do ícone</mat-label>
1953
- <input matInput [formControl]="form.controls.iconColor" placeholder="ex: #8ab4ff" />
1954
- </mat-form-field>
1955
- <mat-form-field appearance="outline">
1956
- <mat-label>Fundo do corpo</mat-label>
1957
- <input matInput [formControl]="form.controls.bodyBg" placeholder="ex: transparent" />
1958
- </mat-form-field>
1959
- <mat-form-field appearance="outline">
1960
- <mat-label>Cor do texto do corpo</mat-label>
1961
- <input matInput [formControl]="form.controls.bodyColor" placeholder="ex: #e6edf3" />
1962
- </mat-form-field>
1963
- <mat-form-field appearance="outline">
1964
- <mat-label>Padding do corpo</mat-label>
1965
- <input matInput [formControl]="form.controls.bodyPadding" placeholder="ex: 10px 12px 12px" />
1966
- </mat-form-field>
1967
- <mat-form-field appearance="outline">
1968
- <mat-label>Tamanho do título</mat-label>
1969
- <input matInput [formControl]="form.controls.titleSize" placeholder="ex: 14px" />
1970
- </mat-form-field>
1971
- <mat-form-field appearance="outline">
1972
- <mat-label>Peso do título</mat-label>
1973
- <input matInput [formControl]="form.controls.titleWeight" placeholder="ex: 600" />
1974
- </mat-form-field>
1975
- <mat-form-field appearance="outline">
1976
- <mat-label>Tamanho do subtítulo</mat-label>
1977
- <input matInput [formControl]="form.controls.subtitleSize" placeholder="ex: 12px" />
1978
- </mat-form-field>
1979
- </div>
1980
-
1981
- <div class="shell-preview" [ngStyle]="previewVars()">
1982
- <div class="shell-preview-card" [class.no-shell]="form.controls.kind.value === 'none'" [class.expanded]="previewExpanded">
1983
- @if (previewShowHeader()) {
1984
- <div class="shell-preview-header">
1985
- <div class="shell-preview-title">
1986
- @if (form.controls.icon.value) {
1987
- <mat-icon [praxisIcon]="form.controls.icon.value"></mat-icon>
1988
- }
1989
- <div class="shell-preview-text">
1990
- <div class="shell-preview-title-text">{{ previewTitle() }}</div>
1991
- <div class="shell-preview-subtitle">{{ previewSubtitle() }}</div>
1992
- </div>
1993
- </div>
1994
- <div class="shell-preview-actions">
1995
- @for (action of previewHeaderActions(); track action.id) {
1996
- <button
1997
- type="button"
1998
- [disabled]="action.disabled"
1999
- [ngClass]="action.variant === 'outlined' ? 'pdx-preview-outlined' : 'pdx-preview-text'"
2000
- mat-button
2001
- >
2002
- @if (action.icon) {
2003
- <mat-icon [praxisIcon]="action.icon"></mat-icon>
2004
- }
2005
- <span class="pdx-preview-label">{{ action.label || action.id }}</span>
2006
- </button>
2007
- }
2008
- @if (previewOverflowCount()) {
2009
- <button
2010
- praxisIconButton="more_horiz"
2011
- size="compact"
2012
- type="button"
2013
- aria-label="Mais ações"
2014
- ></button>
2015
- }
2016
- @for (action of previewWindowActions(); track action.id) {
2017
- @if (action.icon) {
2018
- <button
2019
- [praxisIconButton]="action.icon"
2020
- size="compact"
2021
- type="button"
2022
- [disabled]="action.disabled"
2023
- [attr.aria-label]="action.label || action.id"
2024
- ></button>
2025
- }
2026
- }
2027
- <button
2028
- [praxisIconButton]="previewCollapsed ? 'unfold_more' : 'unfold_less'"
2029
- size="compact"
2030
- type="button"
2031
- (click)="togglePreviewCollapse()"
2032
- aria-label="Recolher"
2033
- ></button>
2034
- <button
2035
- [praxisIconButton]="previewExpanded ? 'close_fullscreen' : 'open_in_full'"
2036
- size="compact"
2037
- type="button"
2038
- (click)="togglePreviewExpand()"
2039
- aria-label="Expandir"
2040
- ></button>
2041
- </div>
2042
- </div>
2043
- }
2044
- <div class="shell-preview-body" [class.hidden]="previewCollapsed">Conteudo do widget</div>
2045
- </div>
2046
- </div>
2047
-
2048
- <mat-divider></mat-divider>
2049
-
2050
- @if (hasAvailableActions) {
2051
- <div class="shell-section">Ações disponíveis</div>
2052
- }
2053
- @if (hasAvailableActions) {
2054
- <div class="shell-available">
2055
- <div class="shell-available-toolbar">
2056
- <mat-form-field appearance="outline">
2057
- <mat-label>Buscar ação</mat-label>
2058
- <input matInput [formControl]="actionQuery" placeholder="ex: export, editar, submit" />
2059
- </mat-form-field>
2060
- <div class="shell-available-filters">
2061
- <mat-checkbox [(ngModel)]="showScopeShell">Shell</mat-checkbox>
2062
- <mat-checkbox [(ngModel)]="showScopeAny">Any</mat-checkbox>
2063
- <mat-checkbox [(ngModel)]="showScopeToolbar">Toolbar</mat-checkbox>
2064
- <mat-checkbox [(ngModel)]="showScopeContext">Context</mat-checkbox>
2065
- </div>
2066
- </div>
2067
- <div class="shell-available-list">
2068
- @for (action of filteredAvailableActions(); track action) {
2069
- <div class="shell-available-item">
2070
- <div class="shell-available-main">
2071
- <div class="shell-available-title">
2072
- @if (action.icon) {
2073
- <mat-icon [praxisIcon]="action.icon"></mat-icon>
2074
- }
2075
- <div class="shell-available-text">
2076
- <div class="shell-available-label">{{ action.label }}</div>
2077
- <div class="shell-available-meta">
2078
- <span class="shell-badge">{{ action.kind === 'command' ? 'Comando' : 'Ação' }}</span>
2079
- <span class="shell-badge">{{ scopeLabel(action.scope) }}</span>
2080
- <span class="shell-code">id: {{ action.id }}</span>
2081
- @if (action.emit) {
2082
- <span class="shell-code">emit: {{ action.emit }}</span>
2083
- }
2084
- @if (action.command) {
2085
- <span class="shell-code">command: {{ action.command }}</span>
2086
- }
2087
- </div>
2088
- </div>
2089
- </div>
2090
- @if (action.description) {
2091
- <div class="shell-available-desc">{{ action.description }}</div>
2092
- }
2093
- @if (action.payloadExample !== undefined) {
2094
- <div class="shell-available-payload">
2095
- <span class="shell-code">payload: {{ stringifyPayload(action.payloadExample) }}</span>
2096
- </div>
2097
- }
2098
- @if (action.scope === 'context') {
2099
- <div class="shell-available-warning">
2100
- Essa ação depende de item/linha selecionado; payload fixo pode não funcionar.
2101
- </div>
2102
- }
2103
- </div>
2104
- <div class="shell-available-actions">
2105
- <button
2106
- mat-stroked-button
2107
- type="button"
2108
- [color]="isActionSelected(action) ? 'warn' : 'primary'"
2109
- (click)="toggleAvailableAction(action)"
2110
- >
2111
- {{ isActionSelected(action) ? 'Remover' : 'Adicionar' }}
2112
- </button>
2113
- </div>
2114
- </div>
2115
- }
2116
- </div>
2117
- </div>
2118
- } @else {
2119
- <div class="shell-empty">Nenhuma ação registrada para este componente.</div>
2120
- }
2121
-
2122
- @if (hasAvailableActions) {
2123
- <mat-divider></mat-divider>
2124
- }
2125
-
2126
- @if (setInputPresets.length) {
2127
- <div class="shell-section">Ações rápidas (inputs)</div>
2128
- }
2129
- @if (setInputPresets.length) {
2130
- <div class="shell-quick-inputs">
2131
- <mat-form-field appearance="outline">
2132
- <mat-label>Input</mat-label>
2133
- <mat-select [formControl]="setInputTarget">
2134
- @for (preset of setInputPresets; track preset) {
2135
- <mat-option [value]="preset.input">
2136
- @if (preset.icon) {
2137
- <mat-icon [praxisIcon]="preset.icon"></mat-icon>
2138
- }
2139
- {{ preset.label }}
2140
- </mat-option>
2141
- }
2142
- </mat-select>
2143
- </mat-form-field>
2144
- @if (setInputTarget.value) {
2145
- <div class="shell-input-path">
2146
- Input: {{ displayInputPath(setInputTarget.value) }}
2147
- </div>
2148
- }
2149
- <mat-form-field appearance="outline">
2150
- <mat-label>Valor</mat-label>
2151
- <mat-select [formControl]="setInputValue">
2152
- @for (option of currentSetInputValues(); track option) {
2153
- <mat-option [value]="option.value">
2154
- @if (option.icon) {
2155
- <mat-icon [praxisIcon]="option.icon"></mat-icon>
2156
- }
2157
- {{ option.label }}
2158
- </mat-option>
2159
- }
2160
- </mat-select>
2161
- </mat-form-field>
2162
- <button mat-flat-button color="primary" type="button" (click)="addSetInputAction()">
2163
- Adicionar ação
2164
- </button>
2165
- </div>
2166
- }
2167
-
2168
- <div class="shell-section shell-actions-head">
2169
- <div>Ações do cabeçalho</div>
2170
- <button mat-flat-button color="primary" type="button" (click)="addAction()">Adicionar ação</button>
2171
- </div>
2172
-
2173
- @if (actions.controls.length) {
2174
- <div class="shell-actions">
2175
- @for (action of actions.controls; track action; let i = $index) {
2176
- <div class="shell-action" [formGroup]="action">
2177
- <div class="shell-action-row">
2178
- <mat-form-field appearance="outline">
2179
- <mat-label>ID</mat-label>
2180
- <input matInput formControlName="id" />
2181
- </mat-form-field>
2182
- <mat-form-field appearance="outline">
2183
- <mat-label>Label</mat-label>
2184
- <input matInput formControlName="label" />
2185
- </mat-form-field>
2186
- <mat-form-field appearance="outline">
2187
- <mat-label>Ícone</mat-label>
2188
- <input matInput formControlName="icon" />
2189
- </mat-form-field>
2190
- </div>
2191
- <div class="shell-action-row">
2192
- <mat-form-field appearance="outline">
2193
- <mat-label>Variante</mat-label>
2194
- <mat-select formControlName="variant">
2195
- <mat-option value="text">Texto</mat-option>
2196
- <mat-option value="outlined">Outlined</mat-option>
2197
- <mat-option value="icon">Ícone</mat-option>
2198
- </mat-select>
2199
- </mat-form-field>
2200
- <mat-form-field appearance="outline">
2201
- <mat-label>Posição</mat-label>
2202
- <mat-select formControlName="placement">
2203
- <mat-option value="header">Cabeçalho</mat-option>
2204
- <mat-option value="window">Janela</mat-option>
2205
- </mat-select>
2206
- </mat-form-field>
2207
- <mat-form-field appearance="outline">
2208
- <mat-label>Emit</mat-label>
2209
- <input matInput formControlName="emit" placeholder="ex: editar" />
2210
- </mat-form-field>
2211
- <mat-form-field appearance="outline">
2212
- <mat-label>Tooltip</mat-label>
2213
- <input matInput formControlName="tooltip" />
2214
- </mat-form-field>
2215
- </div>
2216
- <div class="shell-action-row">
2217
- <mat-form-field appearance="outline">
2218
- <mat-label>Command</mat-label>
2219
- <input matInput formControlName="command" placeholder="ex: add-item" />
2220
- </mat-form-field>
2221
- <mat-form-field appearance="outline">
2222
- <mat-label>Payload (JSON)</mat-label>
2223
- <textarea matInput rows="2" formControlName="payload" placeholder='ex: {"foo":"bar"}'></textarea>
2224
- </mat-form-field>
2225
- <mat-checkbox formControlName="visible">Visível</mat-checkbox>
2226
- <mat-checkbox formControlName="disabled">Desabilitado</mat-checkbox>
2227
- </div>
2228
- @if (showActionInputPath(action.value)) {
2229
- <div class="shell-action-hint">
2230
- Input: {{ displayInputPath(extractActionInput(action.value)) }}
2231
- </div>
2232
- }
2233
- <div class="shell-action-actions">
2234
- <button mat-stroked-button color="warn" type="button" (click)="removeAction(i)">Remover</button>
2235
- </div>
2236
- </div>
2237
- }
2238
- </div>
2239
- } @else {
2240
- <div class="shell-empty">Nenhuma ação configurada.</div>
2241
- }
2242
-
2243
- </div>
2244
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".shell-editor{display:grid;gap:20px;padding:12px 4px 20px}.shell-intro{margin:0;max-width:68ch;color:var(--md-sys-color-on-surface-variant);font-size:14px;line-height:1.5}.shell-section{margin:4px 0 -8px;color:var(--md-sys-color-on-surface);font-size:14px;font-weight:650;line-height:1.4}.shell-grid{display:grid;gap:12px 16px;grid-template-columns:repeat(2,minmax(0,1fr));align-items:start}.shell-field--wide{grid-column:1 / -1}.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}@media(max-width:640px){.shell-editor{padding-inline:0}.shell-grid{grid-template-columns:minmax(0,1fr)}.shell-field--wide{grid-column:auto}}\n"] }]
2245
- }], ctorParameters: () => [{ type: undefined, decorators: [{
2246
- type: Optional
2247
- }, {
2248
- type: Inject,
2249
- args: [SETTINGS_PANEL_DATA]
2250
- }] }, { type: i2$2.ComponentMetadataRegistry, decorators: [{
2251
- type: Optional
2252
- }] }] });
2253
-
2254
- function buildConnectionsViewerModel(page) {
2255
- const widgets = page?.widgets ?? [];
2256
- const links = page?.composition?.links ?? [];
2257
- const counts = new Map();
2258
- for (const widget of widgets) {
2259
- counts.set(widget.key, { incoming: 0, outgoing: 0 });
2260
- }
2261
- const edges = links.map((link) => {
2262
- const fromWidgetId = link.from.kind === 'component-port' ? link.from.ref.widget : null;
2263
- const toWidgetId = link.to.kind === 'component-port' ? link.to.ref.widget : null;
2264
- const fromNestedPath = link.from.kind === 'component-port' && link.from.ref.nestedPath?.length
2265
- ? clone$2(link.from.ref.nestedPath)
2266
- : null;
2267
- const toNestedPath = link.to.kind === 'component-port' && link.to.ref.nestedPath?.length
2268
- ? clone$2(link.to.ref.nestedPath)
2269
- : null;
2270
- const toStatePath = link.to.kind === 'state' ? link.to.ref.path : null;
2271
- const diagnosticReasons = [];
2272
- if (fromWidgetId && !counts.has(fromWidgetId)) {
2273
- diagnosticReasons.push('missing-source-widget');
2274
- }
2275
- if (toWidgetId && !counts.has(toWidgetId)) {
2276
- diagnosticReasons.push('missing-target-widget');
2277
- }
2278
- if (fromWidgetId) {
2279
- const current = counts.get(fromWidgetId) ?? { incoming: 0, outgoing: 0 };
2280
- current.outgoing += 1;
2281
- counts.set(fromWidgetId, current);
2282
- }
2283
- if (toWidgetId) {
2284
- const current = counts.get(toWidgetId) ?? { incoming: 0, outgoing: 0 };
2285
- current.incoming += 1;
2286
- counts.set(toWidgetId, current);
2287
- }
2288
- return {
2289
- id: link.id,
2290
- fromKind: link.from.kind,
2291
- fromWidgetId,
2292
- fromEndpointId: link.from.kind === 'component-port'
2293
- ? buildComponentEndpointId(link.from.ref.widget, link.from.ref.nestedPath)
2294
- : null,
2295
- fromNestedPath,
2296
- fromPort: link.from.kind === 'component-port' ? link.from.ref.port : null,
2297
- toKind: link.to.kind,
2298
- toWidgetId,
2299
- toEndpointId: link.to.kind === 'component-port'
2300
- ? buildComponentEndpointId(link.to.ref.widget, link.to.ref.nestedPath)
2301
- : null,
2302
- toNestedPath,
2303
- toPort: link.to.kind === 'component-port' ? link.to.ref.port : null,
2304
- toStatePath,
2305
- intent: link.intent,
2306
- hasCondition: !!link.condition,
2307
- hasTransform: !!link.transform?.steps?.length,
2308
- hasPolicy: !!link.policy && Object.keys(link.policy).length > 0,
2309
- hasDiagnostics: diagnosticReasons.length > 0,
2310
- diagnosticReasons,
2311
- link,
2312
- };
2313
- });
2314
- const nodes = widgets.map((widget) => {
2315
- const counter = counts.get(widget.key) ?? { incoming: 0, outgoing: 0 };
2316
- return {
2317
- widgetId: widget.key,
2318
- componentType: widget.definition?.id ?? 'unknown',
2319
- incomingCount: counter.incoming,
2320
- outgoingCount: counter.outgoing,
2321
- };
2322
- });
2323
- return {
2324
- nodes,
2325
- edges,
2326
- totalLinks: edges.length,
2327
- conditionLinks: edges.filter((edge) => edge.hasCondition).length,
2328
- transformLinks: edges.filter((edge) => edge.hasTransform).length,
2329
- policyLinks: edges.filter((edge) => edge.hasPolicy).length,
2330
- diagnosticLinks: edges.filter((edge) => edge.hasDiagnostics).length,
2331
- };
2332
- }
2333
- function buildComponentEndpointId(ownerWidgetKey, nestedPath) {
2334
- if (!nestedPath?.length) {
2335
- return ownerWidgetKey;
2336
- }
2337
- return `${ownerWidgetKey}${nestedPath
2338
- .map((segment) => `/${formatPathSegment$1(segment)}`)
2339
- .join('')}`;
2340
- }
2341
- function formatPathSegment$1(segment) {
2342
- if (segment.kind === 'widget') {
2343
- const identity = segment.key
2344
- || segment.componentType
2345
- || (typeof segment.index === 'number' ? String(segment.index) : 'unknown');
2346
- return `widget:${identity}`;
2347
- }
2348
- return [
2349
- segment.kind,
2350
- segment.id || segment.key || segment.index,
2351
- ].filter((part) => part !== undefined && part !== '').join(':');
2352
- }
2353
- function clone$2(value) {
2354
- if (value == null || typeof value !== 'object')
2355
- return value;
2356
- return JSON.parse(JSON.stringify(value));
2357
- }
2358
-
2359
348
  const PRAXIS_PAGE_BUILDER_EN_US = {
2360
349
  'praxis.pageBuilder.editor.title': 'Page settings',
2361
350
  'praxis.pageBuilder.editor.subtitle': 'Define layout, themes, grouping and declarative state without leaving the canonical dynamic page flow.',
@@ -2515,6 +504,33 @@ const PRAXIS_PAGE_BUILDER_EN_US = {
2515
504
  'praxis.pageBuilder.editor.preview.deviceCanvasHint': 'Existing canvas item overrides are preserved.',
2516
505
  'praxis.pageBuilder.toolbar.preview': 'Preview',
2517
506
  'praxis.pageBuilder.toolbar.exitPreview': 'Back to editing',
507
+ 'praxis.pageBuilder.toolbar.add': 'Insert component',
508
+ 'praxis.pageBuilder.toolbar.save': 'Save page',
509
+ 'praxis.pageBuilder.toolbar.undo': 'Undo',
510
+ 'praxis.pageBuilder.toolbar.redo': 'Redo',
511
+ 'praxis.pageBuilder.toolbar.settings': 'Page settings',
512
+ 'praxis.pageBuilder.palette.title': 'Insert component',
513
+ 'praxis.pageBuilder.palette.subtitle': 'Choose a component to add to the page',
514
+ 'praxis.pageBuilder.palette.available': 'available',
515
+ 'praxis.pageBuilder.palette.searchLabel': 'Search components',
516
+ 'praxis.pageBuilder.palette.searchPlaceholder': 'Name, capability, or readiness',
517
+ 'praxis.pageBuilder.palette.addAria': 'Add',
518
+ 'praxis.pageBuilder.palette.empty': 'No components available',
519
+ 'praxis.pageBuilder.palette.cancel': 'Cancel',
520
+ 'praxis.pageBuilder.palette.badges.configEditor.label': 'Config editor',
521
+ 'praxis.pageBuilder.palette.badges.configEditor.description': 'Publishes a canonical settings-panel editor for its input contract.',
522
+ 'praxis.pageBuilder.palette.badges.aiAuthoring.label': 'AI authoring',
523
+ 'praxis.pageBuilder.palette.badges.aiAuthoring.description': 'Publishes a canonical AI authoring manifest for governed changes.',
524
+ 'praxis.pageBuilder.palette.badges.governed.label': 'Governed',
525
+ 'praxis.pageBuilder.palette.badges.governed.description': 'Exposes semantic ports, actions, or commands for governed composition.',
526
+ 'praxis.pageBuilder.palette.badges.preset.label': 'Preset',
527
+ 'praxis.pageBuilder.palette.badges.preset.description': 'Inserts a curated preset without changing the component contract.',
528
+ 'praxis.pageBuilder.palette.badges.dataSource.label': 'Data source',
529
+ 'praxis.pageBuilder.palette.badges.dataSource.description': 'Uses schema, API, resource, or dataset context.',
530
+ 'praxis.pageBuilder.palette.badges.legacy.label': 'Legacy',
531
+ 'praxis.pageBuilder.palette.badges.legacy.description': 'Retained for compatibility; avoid as the default for new canonical authoring.',
532
+ 'praxis.pageBuilder.palette.badges.hostDemo.label': 'Host/demo',
533
+ 'praxis.pageBuilder.palette.badges.hostDemo.description': 'Depends on host/demo semantics and may need governance before public reuse.',
2518
534
  'praxis.pageBuilder.builderMode.editing': 'Editing',
2519
535
  'praxis.pageBuilder.builderMode.connections': 'Connections',
2520
536
  'praxis.pageBuilder.builderMode.ai': 'AI',
@@ -3142,6 +1158,33 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
3142
1158
  'praxis.pageBuilder.editor.preview.deviceCanvasHint': 'Sobrescritas existentes dos itens do canvas são preservadas.',
3143
1159
  'praxis.pageBuilder.toolbar.preview': 'Pré-visualizar',
3144
1160
  'praxis.pageBuilder.toolbar.exitPreview': 'Voltar para edição',
1161
+ 'praxis.pageBuilder.toolbar.add': 'Inserir componente',
1162
+ 'praxis.pageBuilder.toolbar.save': 'Salvar página',
1163
+ 'praxis.pageBuilder.toolbar.undo': 'Desfazer',
1164
+ 'praxis.pageBuilder.toolbar.redo': 'Refazer',
1165
+ 'praxis.pageBuilder.toolbar.settings': 'Configurações da página',
1166
+ 'praxis.pageBuilder.palette.title': 'Inserir componente',
1167
+ 'praxis.pageBuilder.palette.subtitle': 'Escolha um componente para adicionar à página',
1168
+ 'praxis.pageBuilder.palette.available': 'disponíveis',
1169
+ 'praxis.pageBuilder.palette.searchLabel': 'Buscar componentes',
1170
+ 'praxis.pageBuilder.palette.searchPlaceholder': 'Nome, capacidade ou prontidão',
1171
+ 'praxis.pageBuilder.palette.addAria': 'Adicionar',
1172
+ 'praxis.pageBuilder.palette.empty': 'Nenhum componente disponível',
1173
+ 'praxis.pageBuilder.palette.cancel': 'Cancelar',
1174
+ 'praxis.pageBuilder.palette.badges.configEditor.label': 'Editor de configuração',
1175
+ 'praxis.pageBuilder.palette.badges.configEditor.description': 'Publica um editor canônico de configurações para o contrato de inputs.',
1176
+ 'praxis.pageBuilder.palette.badges.aiAuthoring.label': 'Autoria com IA',
1177
+ 'praxis.pageBuilder.palette.badges.aiAuthoring.description': 'Publica um manifesto canônico de autoria por IA para mudanças governadas.',
1178
+ 'praxis.pageBuilder.palette.badges.governed.label': 'Governado',
1179
+ 'praxis.pageBuilder.palette.badges.governed.description': 'Expõe portas, ações ou comandos semânticos para composição governada.',
1180
+ 'praxis.pageBuilder.palette.badges.preset.label': 'Predefinição',
1181
+ 'praxis.pageBuilder.palette.badges.preset.description': 'Insere uma predefinição curada sem alterar o contrato do componente.',
1182
+ 'praxis.pageBuilder.palette.badges.dataSource.label': 'Fonte de dados',
1183
+ 'praxis.pageBuilder.palette.badges.dataSource.description': 'Usa contexto de schema, API, recurso ou conjunto de dados.',
1184
+ 'praxis.pageBuilder.palette.badges.legacy.label': 'Legado',
1185
+ 'praxis.pageBuilder.palette.badges.legacy.description': 'Mantido por compatibilidade; evite como padrão em nova autoria canônica.',
1186
+ 'praxis.pageBuilder.palette.badges.hostDemo.label': 'Host/demo',
1187
+ 'praxis.pageBuilder.palette.badges.hostDemo.description': 'Depende de semântica do host/demo e pode exigir governança antes do reúso público.',
3145
1188
  'praxis.pageBuilder.builderMode.editing': 'Edição',
3146
1189
  'praxis.pageBuilder.builderMode.connections': 'Conexões',
3147
1190
  'praxis.pageBuilder.builderMode.ai': 'IA',
@@ -3610,36 +1653,2067 @@ const PRAXIS_PAGE_BUILDER_PT_BR = {
3610
1653
  'praxis.pageBuilder.agentic.errors.generic': 'Falha na autoria com IA.',
3611
1654
  };
3612
1655
 
3613
- function createPraxisPageBuilderI18nConfig(options = {}) {
3614
- const dictionaries = {
3615
- 'pt-BR': {
3616
- ...PRAXIS_PAGE_BUILDER_PT_BR,
3617
- ...(options.dictionaries?.['pt-BR'] ?? {}),
3618
- },
3619
- 'en-US': {
3620
- ...PRAXIS_PAGE_BUILDER_EN_US,
3621
- ...(options.dictionaries?.['en-US'] ?? {}),
3622
- },
3623
- };
3624
- for (const [locale, dictionary] of Object.entries(options.dictionaries ?? {})) {
3625
- if (locale === 'pt-BR' || locale === 'en-US')
3626
- continue;
3627
- dictionaries[locale] = { ...(dictionaries[locale] ?? {}), ...dictionary };
1656
+ function createPraxisPageBuilderI18nConfig(options = {}) {
1657
+ const dictionaries = {
1658
+ 'pt-BR': {
1659
+ ...PRAXIS_PAGE_BUILDER_PT_BR,
1660
+ ...(options.dictionaries?.['pt-BR'] ?? {}),
1661
+ },
1662
+ 'en-US': {
1663
+ ...PRAXIS_PAGE_BUILDER_EN_US,
1664
+ ...(options.dictionaries?.['en-US'] ?? {}),
1665
+ },
1666
+ };
1667
+ for (const [locale, dictionary] of Object.entries(options.dictionaries ?? {})) {
1668
+ if (locale === 'pt-BR' || locale === 'en-US')
1669
+ continue;
1670
+ dictionaries[locale] = { ...(dictionaries[locale] ?? {}), ...dictionary };
1671
+ }
1672
+ return {
1673
+ locale: options.locale,
1674
+ fallbackLocale: options.fallbackLocale ?? 'pt-BR',
1675
+ dictionaries,
1676
+ };
1677
+ }
1678
+ function providePraxisPageBuilderI18n(options = {}) {
1679
+ return providePraxisI18n(createPraxisPageBuilderI18nConfig(options));
1680
+ }
1681
+ function resolvePraxisPageBuilderText(i18n, key, fallback) {
1682
+ const namespacedKey = key.startsWith('praxis.pageBuilder.')
1683
+ ? key
1684
+ : `praxis.pageBuilder.${key}`;
1685
+ return i18n.t(namespacedKey, undefined, fallback) || fallback;
1686
+ }
1687
+
1688
+ class ComponentPaletteDialogComponent {
1689
+ dialogRef;
1690
+ registry;
1691
+ i18n;
1692
+ data;
1693
+ query = signal('', ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
1694
+ all = signal([], ...(ngDevMode ? [{ debugName: "all" }] : /* istanbul ignore next */ []));
1695
+ filtered = computed(() => this.applyFilters(), ...(ngDevMode ? [{ debugName: "filtered" }] : /* istanbul ignore next */ []));
1696
+ density = computed(() => {
1697
+ const n = this.filtered().length;
1698
+ if (n <= 4)
1699
+ return 'roomy';
1700
+ if (n > 12)
1701
+ return 'dense';
1702
+ return 'normal';
1703
+ }, ...(ngDevMode ? [{ debugName: "density" }] : /* istanbul ignore next */ []));
1704
+ constructor(dialogRef, registry, i18n, data) {
1705
+ this.dialogRef = dialogRef;
1706
+ this.registry = registry;
1707
+ this.i18n = i18n;
1708
+ this.data = data;
1709
+ }
1710
+ ngOnInit() {
1711
+ this.all.set(this.buildEntries(this.registry.getAll()));
1712
+ }
1713
+ select(entry) {
1714
+ this.dialogRef.close({
1715
+ componentId: entry.componentId,
1716
+ ...(entry.presetId ? { presetId: entry.presetId } : {}),
1717
+ ...(entry.inputs ? { inputs: entry.inputs } : {}),
1718
+ });
1719
+ }
1720
+ trackById = (_, entry) => entry.key;
1721
+ getPreview(entry) {
1722
+ return entry.description;
1723
+ }
1724
+ tx(key, fallback) {
1725
+ return resolvePraxisPageBuilderText(this.i18n, key, fallback);
1726
+ }
1727
+ // Create effect in injection context (field initializer is allowed)
1728
+ _adjustSizeEffect = effect(() => {
1729
+ const n = this.filtered().length;
1730
+ let width = '720px';
1731
+ if (n <= 4)
1732
+ width = '560px';
1733
+ else if (n <= 8)
1734
+ width = '680px';
1735
+ const container = this.dialogRef._containerInstance;
1736
+ if (container && container._config) {
1737
+ container._config.width = width;
1738
+ }
1739
+ }, ...(ngDevMode ? [{ debugName: "_adjustSizeEffect" }] : /* istanbul ignore next */ []));
1740
+ applyFilters() {
1741
+ const list = (this.all() || []);
1742
+ const q = (this.query() || '').toLowerCase();
1743
+ const allowIds = new Set((this.data?.allowedWidgetIds || []).map((s) => s.toLowerCase()));
1744
+ const allowTags = new Set((this.data?.allowedWidgetTags || []).map((s) => s.toLowerCase()));
1745
+ const allowPresets = new Set((this.data?.allowedPresetIds || []).map((s) => s.toLowerCase()));
1746
+ const metadataById = new Map(this.registry.getAll().map((meta) => [meta.id, meta]));
1747
+ return list.filter((entry) => {
1748
+ const meta = metadataById.get(entry.componentId);
1749
+ if (!meta)
1750
+ return false;
1751
+ if (q && !entry.searchText.includes(q)) {
1752
+ return false;
1753
+ }
1754
+ if (allowIds.size > 0 && !allowIds.has((meta.id || '').toLowerCase()))
1755
+ return false;
1756
+ if (allowTags.size > 0 && !meta.tags?.some(t => allowTags.has(t.toLowerCase())))
1757
+ return false;
1758
+ if (entry.presetId && allowPresets.size > 0 && !allowPresets.has(entry.presetId.toLowerCase()))
1759
+ return false;
1760
+ if (typeof this.data?.predicate === 'function' && !this.data.predicate(meta))
1761
+ return false;
1762
+ return true;
1763
+ });
1764
+ }
1765
+ buildEntries(metadata) {
1766
+ return metadata.flatMap((meta) => {
1767
+ const baseEntry = {
1768
+ key: meta.id,
1769
+ componentId: meta.id,
1770
+ title: meta.friendlyName || meta.id,
1771
+ description: meta.description || meta.selector,
1772
+ icon: meta.icon || 'widgets',
1773
+ badges: this.buildBadges(meta, false),
1774
+ searchText: `${meta.id} ${meta.friendlyName || ''} ${meta.description || ''} ${meta.selector || ''} ${this.buildBadgeSearchText(meta, false)}`.toLowerCase(),
1775
+ };
1776
+ const presets = (meta.insertionPresets ?? []).map((preset) => ({
1777
+ key: `${meta.id}::${preset.id}`,
1778
+ componentId: meta.id,
1779
+ presetId: preset.id,
1780
+ title: `${meta.friendlyName || meta.id} · ${preset.label}`,
1781
+ description: preset.description || meta.description || meta.selector,
1782
+ icon: preset.icon || meta.icon || 'widgets',
1783
+ badges: this.buildBadges(meta, true),
1784
+ searchText: `${meta.id} ${meta.friendlyName || ''} ${preset.label} ${preset.description || ''} ${meta.description || ''} ${this.buildBadgeSearchText(meta, true)}`.toLowerCase(),
1785
+ inputs: preset.inputs ? JSON.parse(JSON.stringify(preset.inputs)) : undefined,
1786
+ }));
1787
+ return [baseEntry, ...presets];
1788
+ });
1789
+ }
1790
+ buildBadges(meta, isPreset) {
1791
+ const badges = [];
1792
+ const tags = new Set((meta.tags ?? []).map((tag) => tag.toLowerCase()));
1793
+ if (meta.configEditor?.component) {
1794
+ badges.push({
1795
+ label: this.tx('palette.badges.configEditor.label', 'Config editor'),
1796
+ icon: 'tune',
1797
+ tone: 'ready',
1798
+ description: this.tx('palette.badges.configEditor.description', 'This component publishes a canonical settings-panel editor for its input contract.'),
1799
+ });
1800
+ }
1801
+ if (meta.authoringManifestRef?.componentId) {
1802
+ badges.push({
1803
+ label: this.tx('palette.badges.aiAuthoring.label', 'AI authoring'),
1804
+ icon: 'auto_awesome',
1805
+ tone: 'ready',
1806
+ description: this.tx('palette.badges.aiAuthoring.description', 'This component owner publishes a canonical AI authoring manifest for governed changes.'),
1807
+ });
1808
+ }
1809
+ if (meta.ports?.length || meta.actions?.length || meta.commands?.length) {
1810
+ badges.push({
1811
+ label: this.tx('palette.badges.governed.label', 'Governed'),
1812
+ icon: 'account_tree',
1813
+ tone: 'ready',
1814
+ description: this.tx('palette.badges.governed.description', 'This component exposes semantic ports, actions, or commands for governed composition.'),
1815
+ });
1816
+ }
1817
+ if (isPreset) {
1818
+ badges.push({
1819
+ label: this.tx('palette.badges.preset.label', 'Preset'),
1820
+ icon: 'auto_awesome_motion',
1821
+ tone: 'info',
1822
+ description: this.tx('palette.badges.preset.description', 'This entry inserts a curated preset without changing the component contract.'),
1823
+ });
1824
+ }
1825
+ if (this.hasDataSourceInputs(meta) || tags.has('data') || tags.has('analytics')) {
1826
+ badges.push({
1827
+ label: this.tx('palette.badges.dataSource.label', 'Data source'),
1828
+ icon: 'database',
1829
+ tone: 'info',
1830
+ description: this.tx('palette.badges.dataSource.description', 'This component commonly needs schema, API, resource, or dataset context.'),
1831
+ });
1832
+ }
1833
+ if (tags.has('deprecated') || tags.has('legacy') || /legacy|legado|deprecated/i.test(meta.description || '')) {
1834
+ badges.push({
1835
+ label: this.tx('palette.badges.legacy.label', 'Legacy'),
1836
+ icon: 'history',
1837
+ tone: 'warning',
1838
+ description: this.tx('palette.badges.legacy.description', 'This entry is retained for compatibility and should not be the default for new canonical authoring.'),
1839
+ });
1840
+ }
1841
+ if (tags.has('demo') || tags.has('host') || /host-only|host|demo/i.test(`${meta.friendlyName} ${meta.description || ''}`)) {
1842
+ badges.push({
1843
+ label: this.tx('palette.badges.hostDemo.label', 'Host/demo'),
1844
+ icon: 'science',
1845
+ tone: 'neutral',
1846
+ description: this.tx('palette.badges.hostDemo.description', 'This entry depends on host/demo semantics and may need governance before public reuse.'),
1847
+ });
1848
+ }
1849
+ return badges;
1850
+ }
1851
+ buildBadgeSearchText(meta, isPreset) {
1852
+ return this.buildBadges(meta, isPreset)
1853
+ .map((badge) => `${badge.label} ${badge.description}`)
1854
+ .join(' ');
1855
+ }
1856
+ hasDataSourceInputs(meta) {
1857
+ return (meta.inputs ?? []).some((input) => /resource|schema|api|data(source)?|endpoint|resolver/i.test(input.name));
1858
+ }
1859
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ComponentPaletteDialogComponent, deps: [{ token: i1.MatDialogRef }, { token: i2$2.ComponentMetadataRegistry }, { token: i2$2.PraxisI18nService }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component });
1860
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: ComponentPaletteDialogComponent, isStandalone: true, selector: "praxis-component-palette-dialog", providers: [providePraxisPageBuilderI18n()], ngImport: i0, template: `
1861
+ <h2 mat-dialog-title>
1862
+ <div class="dlg-title">
1863
+ <mat-icon class="dlg-icon" [praxisIcon]="'widgets'"></mat-icon>
1864
+ <div class="dlg-texts">
1865
+ <div class="dlg-head">{{ data?.title || tx('palette.title', 'Inserir componente') }}</div>
1866
+ <div class="dlg-sub">{{ tx('palette.subtitle', 'Escolha um componente para adicionar à página') }} · {{ filtered().length }} {{ tx('palette.available', 'disponíveis') }}</div>
1867
+ </div>
1868
+ </div>
1869
+ </h2>
1870
+ <div mat-dialog-content class="pdx-palette-content">
1871
+ <mat-form-field class="pdx-palette-search" appearance="outline">
1872
+ <mat-label>{{ tx('palette.searchLabel', 'Buscar componente') }}</mat-label>
1873
+ <mat-icon matPrefix [praxisIcon]="'search'"></mat-icon>
1874
+ <input
1875
+ matInput
1876
+ type="search"
1877
+ [ngModel]="query()"
1878
+ (ngModelChange)="query.set($event)"
1879
+ [placeholder]="tx('palette.searchPlaceholder', 'Nome, capability ou readiness')"
1880
+ data-testid="component-palette-search"
1881
+ />
1882
+ </mat-form-field>
1883
+ @if (filtered().length) {
1884
+ <div [class]="'pdx-grid ' + density()">
1885
+ @for (entry of filtered(); track trackById($index, entry)) {
1886
+ <button
1887
+ class="pdx-card mat-elevation-z2"
1888
+ type="button"
1889
+ [attr.aria-label]="tx('palette.addAria', 'Adicionar') + ' ' + entry.title"
1890
+ (click)="select(entry)"
1891
+ >
1892
+ <div class="pdx-card-icon">
1893
+ <mat-icon [praxisIcon]="entry.icon"></mat-icon>
1894
+ </div>
1895
+ <div class="pdx-card-body">
1896
+ <div class="pdx-card-title">{{ entry.title }}</div>
1897
+ <div class="pdx-card-desc">{{ entry.description }}</div>
1898
+ @if (entry.badges.length) {
1899
+ <div class="pdx-card-badges">
1900
+ @for (badge of entry.badges; track badge) {
1901
+ <span
1902
+ class="pdx-card-badge"
1903
+ [class]="'pdx-card-badge ' + badge.tone"
1904
+ [title]="badge.description"
1905
+ >
1906
+ <mat-icon [praxisIcon]="badge.icon" aria-hidden="true"></mat-icon>
1907
+ {{ badge.label }}
1908
+ </span>
1909
+ }
1910
+ </div>
1911
+ }
1912
+ </div>
1913
+ </button>
1914
+ }
1915
+ </div>
1916
+ } @else {
1917
+ <div class="pdx-empty">
1918
+ <mat-icon [praxisIcon]="'sentiment_dissatisfied'"></mat-icon>
1919
+ <div>{{ tx('palette.empty', 'Nenhum componente disponível') }}</div>
1920
+ </div>
1921
+ }
1922
+
1923
+ </div>
1924
+ <div mat-dialog-actions align="end">
1925
+ <button mat-stroked-button color="primary" mat-dialog-close>{{ tx('palette.cancel', 'Cancelar') }}</button>
1926
+ </div>
1927
+ `, isInline: true, styles: [":host{display:block}h2[mat-dialog-title]{margin:0;padding:12px 24px 10px;border-bottom:1px solid var(--md-sys-color-outline-variant)}.dlg-title{display:flex;align-items:center;gap:10px}.dlg-icon{color:var(--md-sys-color-primary)}.dlg-head{font-size:18px;font-weight:600;line-height:1.2}.dlg-sub{font-size:12px;opacity:.85;color:var(--md-sys-color-on-surface-variant)}.pdx-palette-content{min-width:480px;max-width:920px;padding-top:8px}.pdx-palette-search{width:100%;margin-bottom:12px}.pdx-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px}.pdx-grid.dense{grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.pdx-grid.roomy{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px}.pdx-card{appearance:none;position:relative;display:grid;grid-template-columns:44px 1fr;grid-template-rows:auto auto;gap:10px;padding:14px;border:1px solid transparent;border-radius:14px;cursor:pointer;outline:none;color:inherit;font:inherit;min-height:92px;text-align:left;background:var(--md-sys-color-surface);border-color:var(--md-sys-color-outline-variant);transition:box-shadow .2s ease,background .25s ease,transform .06s ease}.pdx-grid.dense .pdx-card{padding:10px;border-radius:12px;min-height:84px}.pdx-grid.roomy .pdx-card{padding:16px;border-radius:16px;min-height:100px}.pdx-card:focus,.pdx-card:hover{box-shadow:var(--mat-elevation-level4);background:var(--md-sys-color-surface-container-low);border-color:var(--md-sys-color-primary)}.pdx-card:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-card:active{transform:translateY(1px)}.pdx-card-icon{grid-row:span 2;display:flex;align-items:center;justify-content:center;color:var(--md-sys-color-primary)}.pdx-card-title{font-weight:600;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pdx-card-desc{color:var(--md-sys-color-on-surface-variant);display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;word-break:break-word}.pdx-card-badges{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}.pdx-card-badge{display:inline-flex;align-items:center;gap:4px;min-height:22px;padding:2px 8px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface-variant);background:var(--md-sys-color-surface-container-highest);font-size:11px;line-height:1.2;font-weight:600;letter-spacing:.01em}.pdx-card-badge mat-icon{width:14px;height:14px;font-size:14px}.pdx-card-badge.ready{color:var(--md-sys-color-primary);border-color:color-mix(in srgb,var(--md-sys-color-primary) 45%,transparent);background:color-mix(in srgb,var(--md-sys-color-primary-container) 72%,transparent)}.pdx-card-badge.info{color:var(--md-sys-color-tertiary);border-color:color-mix(in srgb,var(--md-sys-color-tertiary) 38%,transparent);background:color-mix(in srgb,var(--md-sys-color-tertiary-container) 68%,transparent)}.pdx-card-badge.warning{color:var(--md-sys-color-error);border-color:color-mix(in srgb,var(--md-sys-color-error) 35%,transparent);background:color-mix(in srgb,var(--md-sys-color-error-container) 62%,transparent)}.pdx-empty{padding:24px;display:grid;place-items:center;color:var(--md-sys-color-on-surface-variant);gap:8px}@media(max-width:600px){.pdx-palette-content{min-width:320px}.pdx-grid{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.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: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1928
+ }
1929
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ComponentPaletteDialogComponent, decorators: [{
1930
+ type: Component,
1931
+ args: [{ selector: 'praxis-component-palette-dialog', standalone: true, imports: [FormsModule, MatDialogModule, MatFormFieldModule, MatInputModule, MatIconModule, MatButtonModule, PraxisIconDirective], template: `
1932
+ <h2 mat-dialog-title>
1933
+ <div class="dlg-title">
1934
+ <mat-icon class="dlg-icon" [praxisIcon]="'widgets'"></mat-icon>
1935
+ <div class="dlg-texts">
1936
+ <div class="dlg-head">{{ data?.title || tx('palette.title', 'Inserir componente') }}</div>
1937
+ <div class="dlg-sub">{{ tx('palette.subtitle', 'Escolha um componente para adicionar à página') }} · {{ filtered().length }} {{ tx('palette.available', 'disponíveis') }}</div>
1938
+ </div>
1939
+ </div>
1940
+ </h2>
1941
+ <div mat-dialog-content class="pdx-palette-content">
1942
+ <mat-form-field class="pdx-palette-search" appearance="outline">
1943
+ <mat-label>{{ tx('palette.searchLabel', 'Buscar componente') }}</mat-label>
1944
+ <mat-icon matPrefix [praxisIcon]="'search'"></mat-icon>
1945
+ <input
1946
+ matInput
1947
+ type="search"
1948
+ [ngModel]="query()"
1949
+ (ngModelChange)="query.set($event)"
1950
+ [placeholder]="tx('palette.searchPlaceholder', 'Nome, capability ou readiness')"
1951
+ data-testid="component-palette-search"
1952
+ />
1953
+ </mat-form-field>
1954
+ @if (filtered().length) {
1955
+ <div [class]="'pdx-grid ' + density()">
1956
+ @for (entry of filtered(); track trackById($index, entry)) {
1957
+ <button
1958
+ class="pdx-card mat-elevation-z2"
1959
+ type="button"
1960
+ [attr.aria-label]="tx('palette.addAria', 'Adicionar') + ' ' + entry.title"
1961
+ (click)="select(entry)"
1962
+ >
1963
+ <div class="pdx-card-icon">
1964
+ <mat-icon [praxisIcon]="entry.icon"></mat-icon>
1965
+ </div>
1966
+ <div class="pdx-card-body">
1967
+ <div class="pdx-card-title">{{ entry.title }}</div>
1968
+ <div class="pdx-card-desc">{{ entry.description }}</div>
1969
+ @if (entry.badges.length) {
1970
+ <div class="pdx-card-badges">
1971
+ @for (badge of entry.badges; track badge) {
1972
+ <span
1973
+ class="pdx-card-badge"
1974
+ [class]="'pdx-card-badge ' + badge.tone"
1975
+ [title]="badge.description"
1976
+ >
1977
+ <mat-icon [praxisIcon]="badge.icon" aria-hidden="true"></mat-icon>
1978
+ {{ badge.label }}
1979
+ </span>
1980
+ }
1981
+ </div>
1982
+ }
1983
+ </div>
1984
+ </button>
1985
+ }
1986
+ </div>
1987
+ } @else {
1988
+ <div class="pdx-empty">
1989
+ <mat-icon [praxisIcon]="'sentiment_dissatisfied'"></mat-icon>
1990
+ <div>{{ tx('palette.empty', 'Nenhum componente disponível') }}</div>
1991
+ </div>
1992
+ }
1993
+
1994
+ </div>
1995
+ <div mat-dialog-actions align="end">
1996
+ <button mat-stroked-button color="primary" mat-dialog-close>{{ tx('palette.cancel', 'Cancelar') }}</button>
1997
+ </div>
1998
+ `, providers: [providePraxisPageBuilderI18n()], changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}h2[mat-dialog-title]{margin:0;padding:12px 24px 10px;border-bottom:1px solid var(--md-sys-color-outline-variant)}.dlg-title{display:flex;align-items:center;gap:10px}.dlg-icon{color:var(--md-sys-color-primary)}.dlg-head{font-size:18px;font-weight:600;line-height:1.2}.dlg-sub{font-size:12px;opacity:.85;color:var(--md-sys-color-on-surface-variant)}.pdx-palette-content{min-width:480px;max-width:920px;padding-top:8px}.pdx-palette-search{width:100%;margin-bottom:12px}.pdx-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px}.pdx-grid.dense{grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.pdx-grid.roomy{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px}.pdx-card{appearance:none;position:relative;display:grid;grid-template-columns:44px 1fr;grid-template-rows:auto auto;gap:10px;padding:14px;border:1px solid transparent;border-radius:14px;cursor:pointer;outline:none;color:inherit;font:inherit;min-height:92px;text-align:left;background:var(--md-sys-color-surface);border-color:var(--md-sys-color-outline-variant);transition:box-shadow .2s ease,background .25s ease,transform .06s ease}.pdx-grid.dense .pdx-card{padding:10px;border-radius:12px;min-height:84px}.pdx-grid.roomy .pdx-card{padding:16px;border-radius:16px;min-height:100px}.pdx-card:focus,.pdx-card:hover{box-shadow:var(--mat-elevation-level4);background:var(--md-sys-color-surface-container-low);border-color:var(--md-sys-color-primary)}.pdx-card:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-card:active{transform:translateY(1px)}.pdx-card-icon{grid-row:span 2;display:flex;align-items:center;justify-content:center;color:var(--md-sys-color-primary)}.pdx-card-title{font-weight:600;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pdx-card-desc{color:var(--md-sys-color-on-surface-variant);display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;word-break:break-word}.pdx-card-badges{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}.pdx-card-badge{display:inline-flex;align-items:center;gap:4px;min-height:22px;padding:2px 8px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface-variant);background:var(--md-sys-color-surface-container-highest);font-size:11px;line-height:1.2;font-weight:600;letter-spacing:.01em}.pdx-card-badge mat-icon{width:14px;height:14px;font-size:14px}.pdx-card-badge.ready{color:var(--md-sys-color-primary);border-color:color-mix(in srgb,var(--md-sys-color-primary) 45%,transparent);background:color-mix(in srgb,var(--md-sys-color-primary-container) 72%,transparent)}.pdx-card-badge.info{color:var(--md-sys-color-tertiary);border-color:color-mix(in srgb,var(--md-sys-color-tertiary) 38%,transparent);background:color-mix(in srgb,var(--md-sys-color-tertiary-container) 68%,transparent)}.pdx-card-badge.warning{color:var(--md-sys-color-error);border-color:color-mix(in srgb,var(--md-sys-color-error) 35%,transparent);background:color-mix(in srgb,var(--md-sys-color-error-container) 62%,transparent)}.pdx-empty{padding:24px;display:grid;place-items:center;color:var(--md-sys-color-on-surface-variant);gap:8px}@media(max-width:600px){.pdx-palette-content{min-width:320px}.pdx-grid{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}}\n"] }]
1999
+ }], ctorParameters: () => [{ type: i1.MatDialogRef }, { type: i2$2.ComponentMetadataRegistry }, { type: i2$2.PraxisI18nService }, { type: undefined, decorators: [{
2000
+ type: Inject,
2001
+ args: [MAT_DIALOG_DATA]
2002
+ }] }] });
2003
+
2004
+ const BUILTIN_PRESET_LABELS = {
2005
+ 'dark-glass': 'Dark Glass',
2006
+ 'light-neutral': 'Light Neutral',
2007
+ graphite: 'Graphite',
2008
+ frameless: 'Frameless',
2009
+ 'data-panel': 'Data Panel',
2010
+ 'chart-panel': 'Chart Panel',
2011
+ 'metric-panel': 'Metric Panel',
2012
+ 'executive-card': 'Executive Card',
2013
+ 'filter-bar': 'Filter Bar',
2014
+ };
2015
+ const BUILTIN_PRESET_OPTIONS = Object.keys(BUILTIN_SHELL_PRESETS).map((id) => ({
2016
+ id,
2017
+ label: BUILTIN_PRESET_LABELS[id] ?? id,
2018
+ }));
2019
+ const BUILTIN_PRESET_MAP = BUILTIN_SHELL_PRESETS;
2020
+ const SET_INPUT_PRESETS = {
2021
+ 'praxis-dynamic-form': [
2022
+ {
2023
+ input: 'mode',
2024
+ label: 'Modo',
2025
+ icon: 'toggle_on',
2026
+ values: [
2027
+ { label: 'Visualização', value: 'view', icon: 'visibility' },
2028
+ { label: 'Edição', value: 'edit', icon: 'edit' },
2029
+ { label: 'Criação', value: 'create', icon: 'add' },
2030
+ ],
2031
+ },
2032
+ {
2033
+ input: 'schemaSource',
2034
+ label: 'Fonte do schema',
2035
+ icon: 'schema',
2036
+ values: [
2037
+ { label: 'Recurso', value: 'resource' },
2038
+ { label: 'Filtro', value: 'filter' },
2039
+ ],
2040
+ },
2041
+ {
2042
+ input: 'notifyIfOutdated',
2043
+ label: 'Notificação de schema',
2044
+ icon: 'notification_important',
2045
+ values: [
2046
+ { label: 'Inline', value: 'inline' },
2047
+ { label: 'Snackbar', value: 'snackbar' },
2048
+ { label: 'Ambos', value: 'both' },
2049
+ { label: 'Nenhum', value: 'none' },
2050
+ ],
2051
+ },
2052
+ {
2053
+ input: 'removeEmptyContainersOnSave',
2054
+ label: 'Remover containers vazios',
2055
+ icon: 'cleaning_services',
2056
+ values: [
2057
+ { label: 'Ativar', value: true },
2058
+ { label: 'Desativar', value: false },
2059
+ ],
2060
+ },
2061
+ {
2062
+ input: 'autoOpenSettingsOnOutdated',
2063
+ label: 'Abrir editor automático',
2064
+ icon: 'open_in_new',
2065
+ values: [
2066
+ { label: 'Ativar', value: true },
2067
+ { label: 'Desativar', value: false },
2068
+ ],
2069
+ },
2070
+ {
2071
+ input: 'readonlyModeGlobal',
2072
+ label: 'Somente leitura global',
2073
+ icon: 'lock',
2074
+ values: [
2075
+ { label: 'Ativar', value: true },
2076
+ { label: 'Desativar', value: false },
2077
+ ],
2078
+ },
2079
+ {
2080
+ input: 'disabledModeGlobal',
2081
+ label: 'Desabilitar global',
2082
+ icon: 'block',
2083
+ values: [
2084
+ { label: 'Ativar', value: true },
2085
+ { label: 'Desativar', value: false },
2086
+ ],
2087
+ },
2088
+ {
2089
+ input: 'presentationModeGlobal',
2090
+ label: 'Modo apresentação',
2091
+ icon: 'slideshow',
2092
+ values: [
2093
+ { label: 'Ativar', value: true },
2094
+ { label: 'Desativar', value: false },
2095
+ ],
2096
+ },
2097
+ {
2098
+ input: 'visibleGlobal',
2099
+ label: 'Visibilidade global',
2100
+ icon: 'visibility',
2101
+ values: [
2102
+ { label: 'Exibir', value: true },
2103
+ { label: 'Ocultar', value: false },
2104
+ ],
2105
+ },
2106
+ ],
2107
+ 'praxis-table': [
2108
+ {
2109
+ input: 'showToolbar',
2110
+ label: 'Toolbar',
2111
+ icon: 'toolbar',
2112
+ values: [
2113
+ { label: 'Exibir', value: true },
2114
+ { label: 'Ocultar', value: false },
2115
+ ],
2116
+ },
2117
+ {
2118
+ input: 'toolbarV2',
2119
+ label: 'Toolbar v2',
2120
+ icon: 'view_compact',
2121
+ values: [
2122
+ { label: 'Ativar', value: true },
2123
+ { label: 'Desativar', value: false },
2124
+ ],
2125
+ },
2126
+ {
2127
+ input: 'autoDelete',
2128
+ label: 'Auto delete',
2129
+ icon: 'delete_forever',
2130
+ values: [
2131
+ { label: 'Ativar', value: true },
2132
+ { label: 'Desativar', value: false },
2133
+ ],
2134
+ },
2135
+ {
2136
+ input: 'dense',
2137
+ label: 'Modo compacto',
2138
+ icon: 'compress',
2139
+ values: [
2140
+ { label: 'Ativar', value: true },
2141
+ { label: 'Desativar', value: false },
2142
+ ],
2143
+ },
2144
+ ],
2145
+ 'praxis-stepper': [
2146
+ {
2147
+ input: 'inputs.config.orientation',
2148
+ label: 'Orientação',
2149
+ icon: 'swap_vert',
2150
+ values: [
2151
+ { label: 'Horizontal', value: 'horizontal' },
2152
+ { label: 'Vertical', value: 'vertical' },
2153
+ ],
2154
+ },
2155
+ {
2156
+ input: 'inputs.config.headerPosition',
2157
+ label: 'Posição do cabeçalho',
2158
+ icon: 'vertical_align_top',
2159
+ values: [
2160
+ { label: 'Topo', value: 'top' },
2161
+ { label: 'Base', value: 'bottom' },
2162
+ ],
2163
+ },
2164
+ {
2165
+ input: 'inputs.config.linear',
2166
+ label: 'Modo linear',
2167
+ icon: 'linear_scale',
2168
+ values: [
2169
+ { label: 'Ativar', value: true },
2170
+ { label: 'Desativar', value: false },
2171
+ ],
2172
+ },
2173
+ {
2174
+ input: 'inputs.config.navigation.variant',
2175
+ label: 'Navegação (variante)',
2176
+ icon: 'tune',
2177
+ values: [
2178
+ { label: 'Basic', value: 'basic' },
2179
+ { label: 'Flat', value: 'flat' },
2180
+ { label: 'Stroked', value: 'stroked' },
2181
+ { label: 'Raised', value: 'raised' },
2182
+ ],
2183
+ },
2184
+ {
2185
+ input: 'inputs.config.navigation.align',
2186
+ label: 'Navegação (alinhamento)',
2187
+ icon: 'format_align_center',
2188
+ values: [
2189
+ { label: 'Início', value: 'start' },
2190
+ { label: 'Centro', value: 'center' },
2191
+ { label: 'Fim', value: 'end' },
2192
+ { label: 'Espaçado', value: 'space-between' },
2193
+ ],
2194
+ },
2195
+ {
2196
+ input: 'labelPosition',
2197
+ label: 'Posição do rótulo',
2198
+ icon: 'format_align_center',
2199
+ values: [
2200
+ { label: 'Abaixo', value: 'bottom' },
2201
+ { label: 'Ao lado', value: 'end' },
2202
+ ],
2203
+ },
2204
+ {
2205
+ input: 'density',
2206
+ label: 'Densidade',
2207
+ icon: 'density_small',
2208
+ values: [
2209
+ { label: 'Padrão', value: 'default' },
2210
+ { label: 'Confortável', value: 'comfortable' },
2211
+ { label: 'Compacto', value: 'compact' },
2212
+ ],
2213
+ },
2214
+ ],
2215
+ 'praxis-expansion': [
2216
+ {
2217
+ input: 'strictValidation',
2218
+ label: 'Validação estrita',
2219
+ icon: 'rule',
2220
+ values: [
2221
+ { label: 'Ativar', value: true },
2222
+ { label: 'Desativar', value: false },
2223
+ ],
2224
+ },
2225
+ ],
2226
+ };
2227
+ class WidgetShellEditorComponent {
2228
+ registry;
2229
+ form = new FormGroup({
2230
+ kind: new FormControl('dashboard-card', { nonNullable: true }),
2231
+ preset: new FormControl('', { nonNullable: true }),
2232
+ applyToAll: new FormControl(false, { nonNullable: true }),
2233
+ title: new FormControl('', { nonNullable: true }),
2234
+ subtitle: new FormControl('', { nonNullable: true }),
2235
+ icon: new FormControl('', { nonNullable: true }),
2236
+ showHeader: new FormControl('auto', { nonNullable: true }),
2237
+ collapsible: new FormControl(true, { nonNullable: true }),
2238
+ cardBg: new FormControl('', { nonNullable: true }),
2239
+ cardBorder: new FormControl('', { nonNullable: true }),
2240
+ cardRadius: new FormControl('', { nonNullable: true }),
2241
+ cardShadow: new FormControl('', { nonNullable: true }),
2242
+ headerBg: new FormControl('', { nonNullable: true }),
2243
+ headerBorder: new FormControl('', { nonNullable: true }),
2244
+ titleColor: new FormControl('', { nonNullable: true }),
2245
+ subtitleColor: new FormControl('', { nonNullable: true }),
2246
+ iconColor: new FormControl('', { nonNullable: true }),
2247
+ bodyBg: new FormControl('', { nonNullable: true }),
2248
+ bodyColor: new FormControl('', { nonNullable: true }),
2249
+ bodyPadding: new FormControl('', { nonNullable: true }),
2250
+ titleSize: new FormControl('', { nonNullable: true }),
2251
+ titleWeight: new FormControl('', { nonNullable: true }),
2252
+ subtitleSize: new FormControl('', { nonNullable: true }),
2253
+ actions: new FormArray([]),
2254
+ });
2255
+ initialShell = new BehaviorSubject(null);
2256
+ dirtySubject = new BehaviorSubject(false);
2257
+ validSubject = new BehaviorSubject(true);
2258
+ busySubject = new BehaviorSubject(false);
2259
+ isDirty$ = this.dirtySubject.asObservable();
2260
+ isValid$ = this.validSubject.asObservable();
2261
+ isBusy$ = this.busySubject.asObservable();
2262
+ snapshot = '';
2263
+ initialTitle;
2264
+ initialSubtitle;
2265
+ actionCopySources = new WeakMap();
2266
+ presetOptions = [...BUILTIN_PRESET_OPTIONS];
2267
+ presets = { ...BUILTIN_PRESET_MAP };
2268
+ availableActions = [];
2269
+ actionQuery = new FormControl('', { nonNullable: true });
2270
+ showScopeShell = true;
2271
+ showScopeAny = true;
2272
+ showScopeToolbar = false;
2273
+ showScopeContext = false;
2274
+ setInputPresets = [];
2275
+ setInputTarget = new FormControl('', { nonNullable: true });
2276
+ setInputValue = new FormControl(null);
2277
+ previewCollapsed = false;
2278
+ previewExpanded = false;
2279
+ pagePreset = null;
2280
+ constructor(data, registry) {
2281
+ this.registry = registry;
2282
+ const customPresets = data?.presets || {};
2283
+ const customOptions = Object.keys(customPresets).map((id) => ({ id, label: id }));
2284
+ this.presetOptions = [...BUILTIN_PRESET_OPTIONS, ...customOptions];
2285
+ this.presets = { ...BUILTIN_PRESET_MAP, ...customPresets };
2286
+ this.pagePreset = data?.pagePreset || null;
2287
+ const shell = data?.shell || null;
2288
+ this.initialShell.next(shell);
2289
+ this.applyShell(shell);
2290
+ this.loadAvailableActions(data?.widgetType || null);
2291
+ this.loadSetInputPresets(data?.widgetType || null);
2292
+ this.form.valueChanges
2293
+ .pipe(takeUntilDestroyed())
2294
+ .subscribe(() => {
2295
+ this.validSubject.next(this.form.valid);
2296
+ const current = this.snapshotValue();
2297
+ this.dirtySubject.next(current !== this.snapshot);
2298
+ });
2299
+ this.form.controls.preset.valueChanges
2300
+ .pipe(takeUntilDestroyed())
2301
+ .subscribe((value) => {
2302
+ if (!value && this.form.controls.applyToAll.value) {
2303
+ this.form.controls.applyToAll.setValue(false, { emitEvent: false });
2304
+ }
2305
+ });
2306
+ this.setInputTarget.valueChanges
2307
+ .pipe(takeUntilDestroyed())
2308
+ .subscribe(() => {
2309
+ const next = this.currentSetInputValues()[0];
2310
+ if (next)
2311
+ this.setInputValue.setValue(next.value, { emitEvent: false });
2312
+ });
2313
+ }
2314
+ get actions() {
2315
+ return this.form.controls.actions;
2316
+ }
2317
+ get hasAvailableActions() {
2318
+ return !!this.availableActions.length;
2319
+ }
2320
+ addAction(seed) {
2321
+ this.actions.push(this.createActionGroup(seed));
2322
+ }
2323
+ removeAction(index) {
2324
+ this.actions.removeAt(index);
2325
+ }
2326
+ getSettingsValue() {
2327
+ return this.buildResult();
2328
+ }
2329
+ onSave() {
2330
+ return this.buildResult();
2331
+ }
2332
+ reset() {
2333
+ const shell = this.initialShell.value;
2334
+ this.applyShell(shell);
2335
+ }
2336
+ applyShell(shell) {
2337
+ this.initialTitle = shell?.title;
2338
+ this.initialSubtitle = shell?.subtitle;
2339
+ const showHeader = shell?.showHeader == null ? 'auto' : shell.showHeader ? 'true' : 'false';
2340
+ const window = shell?.windowActions || {};
2341
+ const appearance = shell?.appearance || {};
2342
+ const card = appearance.card || {};
2343
+ const header = appearance.header || {};
2344
+ const body = appearance.body || {};
2345
+ const typography = appearance.typography || {};
2346
+ this.form.setControl('actions', new FormArray((shell?.actions || []).map((a) => this.createActionGroup(a))));
2347
+ this.form.setValue({
2348
+ kind: shell?.kind === 'none' ? 'none' : 'dashboard-card',
2349
+ preset: shell?.preset || '',
2350
+ applyToAll: false,
2351
+ title: this.editableText(shell?.title),
2352
+ subtitle: this.editableText(shell?.subtitle),
2353
+ icon: shell?.icon || '',
2354
+ showHeader,
2355
+ collapsible: window.collapsible !== false,
2356
+ cardBg: card.background || '',
2357
+ cardBorder: card.borderColor || '',
2358
+ cardRadius: card.borderRadius || '',
2359
+ cardShadow: card.shadow || '',
2360
+ headerBg: header.background || '',
2361
+ headerBorder: header.borderColor || '',
2362
+ titleColor: header.titleColor || '',
2363
+ subtitleColor: header.subtitleColor || '',
2364
+ iconColor: header.iconColor || '',
2365
+ bodyBg: body.background || '',
2366
+ bodyColor: body.textColor || '',
2367
+ bodyPadding: body.padding || '',
2368
+ titleSize: typography.titleSize || '',
2369
+ titleWeight: typography.titleWeight || '',
2370
+ subtitleSize: typography.subtitleSize || '',
2371
+ actions: this.actions.value,
2372
+ }, { emitEvent: false });
2373
+ this.snapshot = this.snapshotValue();
2374
+ this.dirtySubject.next(false);
2375
+ this.validSubject.next(this.form.valid);
2376
+ }
2377
+ loadAvailableActions(widgetType) {
2378
+ if (!widgetType || !this.registry) {
2379
+ this.availableActions = [];
2380
+ return;
2381
+ }
2382
+ const meta = this.registry.get(widgetType);
2383
+ const actions = (meta?.actions || []).map((action) => ({
2384
+ kind: 'action',
2385
+ id: action.id,
2386
+ label: action.label || action.id,
2387
+ icon: action.icon,
2388
+ scope: action.scope || 'any',
2389
+ emit: action.emit,
2390
+ command: action.command,
2391
+ description: action.description,
2392
+ payloadExample: action.payloadSchema?.example,
2393
+ }));
2394
+ const commands = (meta?.commands || []).map((command) => ({
2395
+ kind: 'command',
2396
+ id: command.name,
2397
+ label: command.label || command.name,
2398
+ icon: command.icon,
2399
+ scope: 'any',
2400
+ command: command.name,
2401
+ description: command.description,
2402
+ payloadExample: command.payloadSchema?.example,
2403
+ }));
2404
+ this.availableActions = [...actions, ...commands];
2405
+ }
2406
+ createActionGroup(seed) {
2407
+ const group = new FormGroup({
2408
+ id: new FormControl(seed?.id || '', { nonNullable: true, validators: [Validators.required] }),
2409
+ label: new FormControl(this.editableText(seed?.label), { nonNullable: true }),
2410
+ icon: new FormControl(seed?.icon || '', { nonNullable: true }),
2411
+ variant: new FormControl(seed?.variant || 'text', { nonNullable: true }),
2412
+ placement: new FormControl(seed?.placement || 'header', { nonNullable: true }),
2413
+ emit: new FormControl(seed?.emit || '', { nonNullable: true }),
2414
+ tooltip: new FormControl(this.editableText(seed?.tooltip), { nonNullable: true }),
2415
+ command: new FormControl(seed?.command || '', { nonNullable: true }),
2416
+ payload: new FormControl(seed?.payload != null ? JSON.stringify(seed.payload) : '', { nonNullable: true }),
2417
+ visible: new FormControl(seed?.visible !== false, { nonNullable: true }),
2418
+ disabled: new FormControl(!!seed?.disabled, { nonNullable: true }),
2419
+ });
2420
+ this.actionCopySources.set(group, { label: seed?.label, tooltip: seed?.tooltip });
2421
+ return group;
2422
+ }
2423
+ buildShell() {
2424
+ const raw = this.form.getRawValue();
2425
+ if (raw.kind === 'none') {
2426
+ return { kind: 'none' };
2427
+ }
2428
+ const shell = {};
2429
+ if (raw.preset)
2430
+ shell.preset = raw.preset;
2431
+ const title = this.preserveTextDescriptor(this.initialTitle, raw.title);
2432
+ const subtitle = this.preserveTextDescriptor(this.initialSubtitle, raw.subtitle);
2433
+ if (title)
2434
+ shell.title = title;
2435
+ if (subtitle)
2436
+ shell.subtitle = subtitle;
2437
+ if (raw.icon.trim())
2438
+ shell.icon = raw.icon.trim();
2439
+ if (raw.showHeader !== 'auto')
2440
+ shell.showHeader = raw.showHeader === 'true';
2441
+ const windowActions = {};
2442
+ if (!raw.collapsible)
2443
+ windowActions.collapsible = false;
2444
+ if (Object.keys(windowActions).length)
2445
+ shell.windowActions = windowActions;
2446
+ const actions = this.actions.controls
2447
+ .map((group) => this.normalizeAction(group.getRawValue(), this.actionCopySources.get(group)))
2448
+ .filter((a) => a.id);
2449
+ if (actions.length)
2450
+ shell.actions = actions;
2451
+ const appearance = this.buildAppearance(raw);
2452
+ if (appearance)
2453
+ shell.appearance = appearance;
2454
+ return shell;
2455
+ }
2456
+ buildResult() {
2457
+ const raw = this.form.getRawValue();
2458
+ const shell = this.buildShell();
2459
+ const applyToAll = !!raw.applyToAll && !!raw.preset;
2460
+ return {
2461
+ shell,
2462
+ applyToAll,
2463
+ pagePreset: applyToAll ? raw.preset : undefined,
2464
+ };
2465
+ }
2466
+ buildAppearance(raw) {
2467
+ const card = {};
2468
+ const header = {};
2469
+ const body = {};
2470
+ const typography = {};
2471
+ if (raw.cardBg)
2472
+ card.background = raw.cardBg.trim();
2473
+ if (raw.cardBorder)
2474
+ card.borderColor = raw.cardBorder.trim();
2475
+ if (raw.cardRadius)
2476
+ card.borderRadius = raw.cardRadius.trim();
2477
+ if (raw.cardShadow)
2478
+ card.shadow = raw.cardShadow.trim();
2479
+ if (raw.headerBg)
2480
+ header.background = raw.headerBg.trim();
2481
+ if (raw.headerBorder)
2482
+ header.borderColor = raw.headerBorder.trim();
2483
+ if (raw.titleColor)
2484
+ header.titleColor = raw.titleColor.trim();
2485
+ if (raw.subtitleColor)
2486
+ header.subtitleColor = raw.subtitleColor.trim();
2487
+ if (raw.iconColor)
2488
+ header.iconColor = raw.iconColor.trim();
2489
+ if (raw.bodyBg)
2490
+ body.background = raw.bodyBg.trim();
2491
+ if (raw.bodyColor)
2492
+ body.textColor = raw.bodyColor.trim();
2493
+ if (raw.bodyPadding)
2494
+ body.padding = raw.bodyPadding.trim();
2495
+ if (raw.titleSize)
2496
+ typography.titleSize = raw.titleSize.trim();
2497
+ if (raw.titleWeight)
2498
+ typography.titleWeight = raw.titleWeight.trim();
2499
+ if (raw.subtitleSize)
2500
+ typography.subtitleSize = raw.subtitleSize.trim();
2501
+ const appearance = {};
2502
+ if (Object.keys(card).length)
2503
+ appearance.card = card;
2504
+ if (Object.keys(header).length)
2505
+ appearance.header = header;
2506
+ if (Object.keys(body).length)
2507
+ appearance.body = body;
2508
+ if (Object.keys(typography).length)
2509
+ appearance.typography = typography;
2510
+ return Object.keys(appearance).length ? appearance : undefined;
2511
+ }
2512
+ previewVars() {
2513
+ const appearance = this.buildPreviewAppearance();
2514
+ return {
2515
+ '--pdx-shell-card-bg': appearance?.card?.background || null,
2516
+ '--pdx-shell-card-border': appearance?.card?.borderColor || null,
2517
+ '--pdx-shell-card-radius': appearance?.card?.borderRadius || null,
2518
+ '--pdx-shell-card-shadow': appearance?.card?.shadow || null,
2519
+ '--pdx-shell-header-bg': appearance?.header?.background || null,
2520
+ '--pdx-shell-header-border': appearance?.header?.borderColor || null,
2521
+ '--pdx-shell-title-color': appearance?.header?.titleColor || null,
2522
+ '--pdx-shell-subtitle-color': appearance?.header?.subtitleColor || null,
2523
+ '--pdx-shell-icon-color': appearance?.header?.iconColor || null,
2524
+ '--pdx-shell-body-bg': appearance?.body?.background || null,
2525
+ '--pdx-shell-body-color': appearance?.body?.textColor || null,
2526
+ '--pdx-shell-body-padding': appearance?.body?.padding || null,
2527
+ '--pdx-shell-title-size': appearance?.typography?.titleSize || null,
2528
+ '--pdx-shell-title-weight': appearance?.typography?.titleWeight || null,
2529
+ '--pdx-shell-subtitle-size': appearance?.typography?.subtitleSize || null,
2530
+ };
2531
+ }
2532
+ previewShowHeader() {
2533
+ const raw = this.form.getRawValue();
2534
+ if (raw.kind === 'none')
2535
+ return false;
2536
+ if (raw.showHeader === 'true')
2537
+ return true;
2538
+ if (raw.showHeader === 'false')
2539
+ return false;
2540
+ return !!(raw.title || raw.subtitle || raw.icon);
2541
+ }
2542
+ previewTitle() {
2543
+ const title = this.editableText(this.form.controls.title.value).trim();
2544
+ return title || 'Titulo do card';
2545
+ }
2546
+ previewSubtitle() {
2547
+ const subtitle = this.editableText(this.form.controls.subtitle.value).trim();
2548
+ return subtitle || 'Subtitulo opcional';
2549
+ }
2550
+ buildPreviewAppearance() {
2551
+ const raw = this.form.getRawValue();
2552
+ const presetId = raw.preset || this.pagePreset || '';
2553
+ const base = presetId ? this.presets[presetId] : undefined;
2554
+ const override = this.buildAppearance(raw);
2555
+ return this.mergeAppearance(base, override);
2556
+ }
2557
+ mergeAppearance(base, override) {
2558
+ if (!base && !override)
2559
+ return undefined;
2560
+ const out = {};
2561
+ const mergeSection = (key) => {
2562
+ const a = (base && base[key]) || undefined;
2563
+ const b = (override && override[key]) || undefined;
2564
+ if (!a && !b)
2565
+ return;
2566
+ out[key] = { ...(a || {}), ...(b || {}) };
2567
+ };
2568
+ mergeSection('card');
2569
+ mergeSection('header');
2570
+ mergeSection('body');
2571
+ mergeSection('typography');
2572
+ return out;
2573
+ }
2574
+ togglePreviewCollapse() {
2575
+ this.previewCollapsed = !this.previewCollapsed;
2576
+ }
2577
+ togglePreviewExpand() {
2578
+ this.previewExpanded = !this.previewExpanded;
2579
+ }
2580
+ previewHeaderActions() {
2581
+ const actions = this.previewActions().filter((a) => (a.placement || 'header') === 'header');
2582
+ return actions.slice(0, 3);
2583
+ }
2584
+ previewOverflowCount() {
2585
+ const actions = this.previewActions().filter((a) => (a.placement || 'header') === 'header');
2586
+ return Math.max(0, actions.length - 3);
2587
+ }
2588
+ previewWindowActions() {
2589
+ return this.previewActions().filter((a) => (a.placement || 'header') === 'window');
2590
+ }
2591
+ normalizeAction(raw, source) {
2592
+ const action = { id: String(raw.id || '').trim() };
2593
+ const label = String(raw.label || '').trim();
2594
+ const icon = String(raw.icon || '').trim();
2595
+ const emit = String(raw.emit || '').trim();
2596
+ const tooltip = String(raw.tooltip || '').trim();
2597
+ const command = String(raw.command || '').trim();
2598
+ const normalizedLabel = this.preserveTextDescriptor(source?.label, label);
2599
+ const normalizedTooltip = this.preserveTextDescriptor(source?.tooltip, tooltip);
2600
+ if (normalizedLabel)
2601
+ action.label = normalizedLabel;
2602
+ if (icon)
2603
+ action.icon = icon;
2604
+ if (emit)
2605
+ action.emit = emit;
2606
+ if (normalizedTooltip)
2607
+ action.tooltip = normalizedTooltip;
2608
+ if (command)
2609
+ action.command = command;
2610
+ if (raw.variant && raw.variant !== 'text')
2611
+ action.variant = raw.variant;
2612
+ if (raw.placement && raw.placement !== 'header')
2613
+ action.placement = raw.placement;
2614
+ if (raw.visible === false)
2615
+ action.visible = false;
2616
+ if (raw.disabled === true)
2617
+ action.disabled = true;
2618
+ const payload = this.tryParsePayload(raw.payload);
2619
+ if (payload !== undefined)
2620
+ action.payload = payload;
2621
+ return action;
2622
+ }
2623
+ editableText(value) {
2624
+ if (typeof value === 'string')
2625
+ return value;
2626
+ return value?.text || value?.key || '';
2627
+ }
2628
+ preserveTextDescriptor(source, editedValue) {
2629
+ const edited = String(editedValue ?? '').trim();
2630
+ if (!edited)
2631
+ return undefined;
2632
+ if (source && typeof source === 'object') {
2633
+ const authoredText = this.editableText(source).trim();
2634
+ return edited === authoredText ? { ...source } : edited;
2635
+ }
2636
+ return edited;
2637
+ }
2638
+ tryParsePayload(raw) {
2639
+ const value = String(raw || '').trim();
2640
+ if (!value)
2641
+ return undefined;
2642
+ try {
2643
+ return JSON.parse(value);
2644
+ }
2645
+ catch {
2646
+ return value;
2647
+ }
2648
+ }
2649
+ snapshotValue() {
2650
+ try {
2651
+ return JSON.stringify(this.buildResult());
2652
+ }
2653
+ catch {
2654
+ return '';
2655
+ }
2656
+ }
2657
+ filteredAvailableActions() {
2658
+ const query = this.actionQuery.value.trim().toLowerCase();
2659
+ const scopes = new Set();
2660
+ if (this.showScopeShell)
2661
+ scopes.add('shell');
2662
+ if (this.showScopeAny)
2663
+ scopes.add('any');
2664
+ if (this.showScopeToolbar)
2665
+ scopes.add('toolbar');
2666
+ if (this.showScopeContext)
2667
+ scopes.add('context');
2668
+ return this.availableActions.filter((action) => {
2669
+ const scope = action.scope || 'any';
2670
+ if (!scopes.has(scope))
2671
+ return false;
2672
+ if (!query)
2673
+ return true;
2674
+ const hay = [
2675
+ action.label,
2676
+ action.id,
2677
+ action.description,
2678
+ action.emit,
2679
+ action.command,
2680
+ ]
2681
+ .filter(Boolean)
2682
+ .join(' ')
2683
+ .toLowerCase();
2684
+ return hay.includes(query);
2685
+ });
2686
+ }
2687
+ isActionSelected(action) {
2688
+ return this.findActionIndex(action) >= 0;
2689
+ }
2690
+ toggleAvailableAction(action) {
2691
+ const idx = this.findActionIndex(action);
2692
+ if (idx >= 0) {
2693
+ this.removeAction(idx);
2694
+ return;
2695
+ }
2696
+ this.addAction({
2697
+ id: action.id,
2698
+ label: action.label,
2699
+ icon: action.icon,
2700
+ emit: action.emit,
2701
+ command: action.command,
2702
+ payload: action.payloadExample,
2703
+ });
2704
+ }
2705
+ scopeLabel(scope) {
2706
+ const value = scope || 'any';
2707
+ if (value === 'shell')
2708
+ return 'Shell';
2709
+ if (value === 'toolbar')
2710
+ return 'Toolbar';
2711
+ if (value === 'context')
2712
+ return 'Context';
2713
+ return 'Any';
2714
+ }
2715
+ stringifyPayload(payload) {
2716
+ if (payload === null)
2717
+ return 'null';
2718
+ if (payload === undefined)
2719
+ return 'undefined';
2720
+ if (typeof payload === 'string')
2721
+ return payload;
2722
+ try {
2723
+ return JSON.stringify(payload);
2724
+ }
2725
+ catch {
2726
+ return String(payload);
2727
+ }
2728
+ }
2729
+ displayInputPath(path) {
2730
+ const trimmed = String(path || '').trim();
2731
+ if (!trimmed)
2732
+ return '';
2733
+ return trimmed.replace(/^inputs\./, '');
2734
+ }
2735
+ extractActionInput(actionValue) {
2736
+ const payload = this.tryParsePayload(actionValue?.payload);
2737
+ const input = typeof payload?.input === 'string' ? payload.input.trim() : '';
2738
+ return input;
2739
+ }
2740
+ showActionInputPath(actionValue) {
2741
+ const command = String(actionValue?.command || '').trim();
2742
+ if (command !== 'pdx:set-input')
2743
+ return false;
2744
+ return !!this.extractActionInput(actionValue);
2745
+ }
2746
+ currentSetInputValues() {
2747
+ const preset = this.setInputPresets.find((p) => p.input === this.setInputTarget.value);
2748
+ return preset?.values || [];
2749
+ }
2750
+ addSetInputAction() {
2751
+ const preset = this.setInputPresets.find((p) => p.input === this.setInputTarget.value);
2752
+ if (!preset)
2753
+ return;
2754
+ const selected = this.currentSetInputValues().find((v) => v.value === this.setInputValue.value);
2755
+ if (!selected)
2756
+ return;
2757
+ const label = `${preset.label}: ${selected.label}`;
2758
+ const payload = { input: preset.input, value: selected.value };
2759
+ this.addAction({
2760
+ id: `set-${preset.input}-${String(selected.value)}`,
2761
+ label,
2762
+ icon: selected.icon || preset.icon,
2763
+ command: 'pdx:set-input',
2764
+ payload,
2765
+ variant: 'text',
2766
+ placement: 'header',
2767
+ });
2768
+ }
2769
+ findActionIndex(action) {
2770
+ const values = this.actions.getRawValue();
2771
+ return values.findIndex((a) => {
2772
+ if (a.id !== action.id)
2773
+ return false;
2774
+ if (action.command && a.command)
2775
+ return a.command === action.command;
2776
+ if (action.emit && a.emit)
2777
+ return a.emit === action.emit;
2778
+ return true;
2779
+ });
2780
+ }
2781
+ previewActions() {
2782
+ return (this.actions.getRawValue() || [])
2783
+ .map((a) => this.normalizeAction(a))
2784
+ .filter((a) => a.id && a.visible !== false);
2785
+ }
2786
+ loadSetInputPresets(widgetType) {
2787
+ if (!widgetType) {
2788
+ this.setInputPresets = [];
2789
+ return;
2790
+ }
2791
+ this.setInputPresets = SET_INPUT_PRESETS[widgetType] || [];
2792
+ const first = this.setInputPresets[0];
2793
+ if (first) {
2794
+ this.setInputTarget.setValue(first.input, { emitEvent: false });
2795
+ const firstValue = first.values[0];
2796
+ if (firstValue)
2797
+ this.setInputValue.setValue(firstValue.value, { emitEvent: false });
2798
+ }
2799
+ }
2800
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: WidgetShellEditorComponent, deps: [{ token: SETTINGS_PANEL_DATA, optional: true }, { token: i2$2.ComponentMetadataRegistry, optional: true }], target: i0.ɵɵFactoryTarget.Component });
2801
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: WidgetShellEditorComponent, isStandalone: true, selector: "praxis-widget-shell-editor", ngImport: i0, template: `
2802
+ <div class="shell-editor">
2803
+ <p class="shell-intro">
2804
+ Defina a identidade, a aparência e as ações do card. Use a prévia para conferir o resultado antes de salvar.
2805
+ </p>
2806
+
2807
+ <div class="shell-section">Geral</div>
2808
+ <div class="shell-grid">
2809
+ <mat-form-field appearance="outline">
2810
+ <mat-label>Tipo de card</mat-label>
2811
+ <mat-select [formControl]="form.controls.kind">
2812
+ <mat-option value="dashboard-card">Dashboard Card</mat-option>
2813
+ <mat-option value="none">Sem shell</mat-option>
2814
+ </mat-select>
2815
+ </mat-form-field>
2816
+ <mat-form-field appearance="outline">
2817
+ <mat-label>Preset</mat-label>
2818
+ <mat-select [formControl]="form.controls.preset">
2819
+ <mat-option value="">Nenhum</mat-option>
2820
+ @if (pagePreset) {
2821
+ <mat-option [value]="pagePreset">Usar preset da página ({{ pagePreset }})</mat-option>
2822
+ }
2823
+ @for (p of presetOptions; track p) {
2824
+ <mat-option [value]="p.id">{{ p.label }}</mat-option>
2825
+ }
2826
+ </mat-select>
2827
+ </mat-form-field>
2828
+ @if (!form.controls.preset.value && pagePreset) {
2829
+ <div class="shell-hint">
2830
+ Preset da página: {{ pagePreset }}
2831
+ </div>
2832
+ }
2833
+ @if (form.controls.preset.value) {
2834
+ <mat-checkbox [formControl]="form.controls.applyToAll">
2835
+ Aplicar preset como padrão da página
2836
+ </mat-checkbox>
2837
+ }
2838
+ <mat-form-field appearance="outline">
2839
+ <mat-label>Ícone</mat-label>
2840
+ <input matInput [formControl]="form.controls.icon" placeholder="ex: badge" />
2841
+ </mat-form-field>
2842
+ <mat-form-field appearance="outline">
2843
+ <mat-label>Título</mat-label>
2844
+ <input matInput [formControl]="form.controls.title" />
2845
+ </mat-form-field>
2846
+ <mat-form-field appearance="outline" class="shell-field--wide">
2847
+ <mat-label>Subtítulo</mat-label>
2848
+ <input matInput [formControl]="form.controls.subtitle" />
2849
+ </mat-form-field>
2850
+ <mat-form-field appearance="outline">
2851
+ <mat-label>Mostrar cabeçalho</mat-label>
2852
+ <mat-select [formControl]="form.controls.showHeader">
2853
+ <mat-option value="auto">Automático</mat-option>
2854
+ <mat-option value="true">Sempre</mat-option>
2855
+ <mat-option value="false">Nunca</mat-option>
2856
+ </mat-select>
2857
+ </mat-form-field>
2858
+ </div>
2859
+
2860
+ <div class="shell-section">Controles de janela</div>
2861
+ <div class="shell-flags">
2862
+ <mat-checkbox [formControl]="form.controls.collapsible">Permitir recolher</mat-checkbox>
2863
+ </div>
2864
+
2865
+ <mat-divider></mat-divider>
2866
+
2867
+ <div class="shell-section">Aparência</div>
2868
+ <div class="shell-grid">
2869
+ <mat-form-field appearance="outline">
2870
+ <mat-label>Fundo do card</mat-label>
2871
+ <input matInput [formControl]="form.controls.cardBg" placeholder="ex: #0b1220" />
2872
+ </mat-form-field>
2873
+ <mat-form-field appearance="outline">
2874
+ <mat-label>Cor da borda</mat-label>
2875
+ <input matInput [formControl]="form.controls.cardBorder" placeholder="ex: rgba(255,255,255,0.12)" />
2876
+ </mat-form-field>
2877
+ <mat-form-field appearance="outline">
2878
+ <mat-label>Raio da borda</mat-label>
2879
+ <input matInput [formControl]="form.controls.cardRadius" placeholder="ex: 14px" />
2880
+ </mat-form-field>
2881
+ <mat-form-field appearance="outline">
2882
+ <mat-label>Sombra</mat-label>
2883
+ <input matInput [formControl]="form.controls.cardShadow" placeholder="ex: 0 10px 30px rgba(0,0,0,0.2)" />
2884
+ </mat-form-field>
2885
+ <mat-form-field appearance="outline">
2886
+ <mat-label>Fundo do cabeçalho</mat-label>
2887
+ <input matInput [formControl]="form.controls.headerBg" placeholder="ex: linear-gradient(...)" />
2888
+ </mat-form-field>
2889
+ <mat-form-field appearance="outline">
2890
+ <mat-label>Borda do cabeçalho</mat-label>
2891
+ <input matInput [formControl]="form.controls.headerBorder" placeholder="ex: rgba(255,255,255,0.08)" />
2892
+ </mat-form-field>
2893
+ <mat-form-field appearance="outline">
2894
+ <mat-label>Cor do título</mat-label>
2895
+ <input matInput [formControl]="form.controls.titleColor" placeholder="ex: #e6edf3" />
2896
+ </mat-form-field>
2897
+ <mat-form-field appearance="outline">
2898
+ <mat-label>Cor do subtítulo</mat-label>
2899
+ <input matInput [formControl]="form.controls.subtitleColor" placeholder="ex: #9fb0c4" />
2900
+ </mat-form-field>
2901
+ <mat-form-field appearance="outline">
2902
+ <mat-label>Cor do ícone</mat-label>
2903
+ <input matInput [formControl]="form.controls.iconColor" placeholder="ex: #8ab4ff" />
2904
+ </mat-form-field>
2905
+ <mat-form-field appearance="outline">
2906
+ <mat-label>Fundo do corpo</mat-label>
2907
+ <input matInput [formControl]="form.controls.bodyBg" placeholder="ex: transparent" />
2908
+ </mat-form-field>
2909
+ <mat-form-field appearance="outline">
2910
+ <mat-label>Cor do texto do corpo</mat-label>
2911
+ <input matInput [formControl]="form.controls.bodyColor" placeholder="ex: #e6edf3" />
2912
+ </mat-form-field>
2913
+ <mat-form-field appearance="outline">
2914
+ <mat-label>Padding do corpo</mat-label>
2915
+ <input matInput [formControl]="form.controls.bodyPadding" placeholder="ex: 10px 12px 12px" />
2916
+ </mat-form-field>
2917
+ <mat-form-field appearance="outline">
2918
+ <mat-label>Tamanho do título</mat-label>
2919
+ <input matInput [formControl]="form.controls.titleSize" placeholder="ex: 14px" />
2920
+ </mat-form-field>
2921
+ <mat-form-field appearance="outline">
2922
+ <mat-label>Peso do título</mat-label>
2923
+ <input matInput [formControl]="form.controls.titleWeight" placeholder="ex: 600" />
2924
+ </mat-form-field>
2925
+ <mat-form-field appearance="outline">
2926
+ <mat-label>Tamanho do subtítulo</mat-label>
2927
+ <input matInput [formControl]="form.controls.subtitleSize" placeholder="ex: 12px" />
2928
+ </mat-form-field>
2929
+ </div>
2930
+
2931
+ <div class="shell-preview" [ngStyle]="previewVars()">
2932
+ <div class="shell-preview-card" [class.no-shell]="form.controls.kind.value === 'none'" [class.expanded]="previewExpanded">
2933
+ @if (previewShowHeader()) {
2934
+ <div class="shell-preview-header">
2935
+ <div class="shell-preview-title">
2936
+ @if (form.controls.icon.value) {
2937
+ <mat-icon [praxisIcon]="form.controls.icon.value"></mat-icon>
2938
+ }
2939
+ <div class="shell-preview-text">
2940
+ <div class="shell-preview-title-text">{{ previewTitle() }}</div>
2941
+ <div class="shell-preview-subtitle">{{ previewSubtitle() }}</div>
2942
+ </div>
2943
+ </div>
2944
+ <div class="shell-preview-actions">
2945
+ @for (action of previewHeaderActions(); track action.id) {
2946
+ <button
2947
+ type="button"
2948
+ [disabled]="action.disabled"
2949
+ [ngClass]="action.variant === 'outlined' ? 'pdx-preview-outlined' : 'pdx-preview-text'"
2950
+ mat-button
2951
+ >
2952
+ @if (action.icon) {
2953
+ <mat-icon [praxisIcon]="action.icon"></mat-icon>
2954
+ }
2955
+ <span class="pdx-preview-label">{{ action.label || action.id }}</span>
2956
+ </button>
2957
+ }
2958
+ @if (previewOverflowCount()) {
2959
+ <button
2960
+ praxisIconButton="more_horiz"
2961
+ size="compact"
2962
+ type="button"
2963
+ aria-label="Mais ações"
2964
+ ></button>
2965
+ }
2966
+ @for (action of previewWindowActions(); track action.id) {
2967
+ @if (action.icon) {
2968
+ <button
2969
+ [praxisIconButton]="action.icon"
2970
+ size="compact"
2971
+ type="button"
2972
+ [disabled]="action.disabled"
2973
+ [attr.aria-label]="action.label || action.id"
2974
+ ></button>
2975
+ }
2976
+ }
2977
+ <button
2978
+ [praxisIconButton]="previewCollapsed ? 'unfold_more' : 'unfold_less'"
2979
+ size="compact"
2980
+ type="button"
2981
+ (click)="togglePreviewCollapse()"
2982
+ aria-label="Recolher"
2983
+ ></button>
2984
+ <button
2985
+ [praxisIconButton]="previewExpanded ? 'close_fullscreen' : 'open_in_full'"
2986
+ size="compact"
2987
+ type="button"
2988
+ (click)="togglePreviewExpand()"
2989
+ aria-label="Expandir"
2990
+ ></button>
2991
+ </div>
2992
+ </div>
2993
+ }
2994
+ <div class="shell-preview-body" [class.hidden]="previewCollapsed">Conteudo do widget</div>
2995
+ </div>
2996
+ </div>
2997
+
2998
+ <mat-divider></mat-divider>
2999
+
3000
+ @if (hasAvailableActions) {
3001
+ <div class="shell-section">Ações disponíveis</div>
3002
+ }
3003
+ @if (hasAvailableActions) {
3004
+ <div class="shell-available">
3005
+ <div class="shell-available-toolbar">
3006
+ <mat-form-field appearance="outline">
3007
+ <mat-label>Buscar ação</mat-label>
3008
+ <input matInput [formControl]="actionQuery" placeholder="ex: export, editar, submit" />
3009
+ </mat-form-field>
3010
+ <div class="shell-available-filters">
3011
+ <mat-checkbox [(ngModel)]="showScopeShell">Shell</mat-checkbox>
3012
+ <mat-checkbox [(ngModel)]="showScopeAny">Any</mat-checkbox>
3013
+ <mat-checkbox [(ngModel)]="showScopeToolbar">Toolbar</mat-checkbox>
3014
+ <mat-checkbox [(ngModel)]="showScopeContext">Context</mat-checkbox>
3015
+ </div>
3016
+ </div>
3017
+ <div class="shell-available-list">
3018
+ @for (action of filteredAvailableActions(); track action) {
3019
+ <div class="shell-available-item">
3020
+ <div class="shell-available-main">
3021
+ <div class="shell-available-title">
3022
+ @if (action.icon) {
3023
+ <mat-icon [praxisIcon]="action.icon"></mat-icon>
3024
+ }
3025
+ <div class="shell-available-text">
3026
+ <div class="shell-available-label">{{ action.label }}</div>
3027
+ <div class="shell-available-meta">
3028
+ <span class="shell-badge">{{ action.kind === 'command' ? 'Comando' : 'Ação' }}</span>
3029
+ <span class="shell-badge">{{ scopeLabel(action.scope) }}</span>
3030
+ <span class="shell-code">id: {{ action.id }}</span>
3031
+ @if (action.emit) {
3032
+ <span class="shell-code">emit: {{ action.emit }}</span>
3033
+ }
3034
+ @if (action.command) {
3035
+ <span class="shell-code">command: {{ action.command }}</span>
3036
+ }
3037
+ </div>
3038
+ </div>
3039
+ </div>
3040
+ @if (action.description) {
3041
+ <div class="shell-available-desc">{{ action.description }}</div>
3042
+ }
3043
+ @if (action.payloadExample !== undefined) {
3044
+ <div class="shell-available-payload">
3045
+ <span class="shell-code">payload: {{ stringifyPayload(action.payloadExample) }}</span>
3046
+ </div>
3047
+ }
3048
+ @if (action.scope === 'context') {
3049
+ <div class="shell-available-warning">
3050
+ Essa ação depende de item/linha selecionado; payload fixo pode não funcionar.
3051
+ </div>
3052
+ }
3053
+ </div>
3054
+ <div class="shell-available-actions">
3055
+ <button
3056
+ mat-stroked-button
3057
+ type="button"
3058
+ [color]="isActionSelected(action) ? 'warn' : 'primary'"
3059
+ (click)="toggleAvailableAction(action)"
3060
+ >
3061
+ {{ isActionSelected(action) ? 'Remover' : 'Adicionar' }}
3062
+ </button>
3063
+ </div>
3064
+ </div>
3065
+ }
3066
+ </div>
3067
+ </div>
3068
+ } @else {
3069
+ <div class="shell-empty">Nenhuma ação registrada para este componente.</div>
3070
+ }
3071
+
3072
+ @if (hasAvailableActions) {
3073
+ <mat-divider></mat-divider>
3074
+ }
3075
+
3076
+ @if (setInputPresets.length) {
3077
+ <div class="shell-section">Ações rápidas (inputs)</div>
3078
+ }
3079
+ @if (setInputPresets.length) {
3080
+ <div class="shell-quick-inputs">
3081
+ <mat-form-field appearance="outline">
3082
+ <mat-label>Input</mat-label>
3083
+ <mat-select [formControl]="setInputTarget">
3084
+ @for (preset of setInputPresets; track preset) {
3085
+ <mat-option [value]="preset.input">
3086
+ @if (preset.icon) {
3087
+ <mat-icon [praxisIcon]="preset.icon"></mat-icon>
3088
+ }
3089
+ {{ preset.label }}
3090
+ </mat-option>
3091
+ }
3092
+ </mat-select>
3093
+ </mat-form-field>
3094
+ @if (setInputTarget.value) {
3095
+ <div class="shell-input-path">
3096
+ Input: {{ displayInputPath(setInputTarget.value) }}
3097
+ </div>
3098
+ }
3099
+ <mat-form-field appearance="outline">
3100
+ <mat-label>Valor</mat-label>
3101
+ <mat-select [formControl]="setInputValue">
3102
+ @for (option of currentSetInputValues(); track option) {
3103
+ <mat-option [value]="option.value">
3104
+ @if (option.icon) {
3105
+ <mat-icon [praxisIcon]="option.icon"></mat-icon>
3106
+ }
3107
+ {{ option.label }}
3108
+ </mat-option>
3109
+ }
3110
+ </mat-select>
3111
+ </mat-form-field>
3112
+ <button mat-flat-button color="primary" type="button" (click)="addSetInputAction()">
3113
+ Adicionar ação
3114
+ </button>
3115
+ </div>
3116
+ }
3117
+
3118
+ <div class="shell-section shell-actions-head">
3119
+ <div>Ações do cabeçalho</div>
3120
+ <button mat-flat-button color="primary" type="button" (click)="addAction()">Adicionar ação</button>
3121
+ </div>
3122
+
3123
+ @if (actions.controls.length) {
3124
+ <div class="shell-actions">
3125
+ @for (action of actions.controls; track action; let i = $index) {
3126
+ <div class="shell-action" [formGroup]="action">
3127
+ <div class="shell-action-row">
3128
+ <mat-form-field appearance="outline">
3129
+ <mat-label>ID</mat-label>
3130
+ <input matInput formControlName="id" />
3131
+ </mat-form-field>
3132
+ <mat-form-field appearance="outline">
3133
+ <mat-label>Label</mat-label>
3134
+ <input matInput formControlName="label" />
3135
+ </mat-form-field>
3136
+ <mat-form-field appearance="outline">
3137
+ <mat-label>Ícone</mat-label>
3138
+ <input matInput formControlName="icon" />
3139
+ </mat-form-field>
3140
+ </div>
3141
+ <div class="shell-action-row">
3142
+ <mat-form-field appearance="outline">
3143
+ <mat-label>Variante</mat-label>
3144
+ <mat-select formControlName="variant">
3145
+ <mat-option value="text">Texto</mat-option>
3146
+ <mat-option value="outlined">Outlined</mat-option>
3147
+ <mat-option value="icon">Ícone</mat-option>
3148
+ </mat-select>
3149
+ </mat-form-field>
3150
+ <mat-form-field appearance="outline">
3151
+ <mat-label>Posição</mat-label>
3152
+ <mat-select formControlName="placement">
3153
+ <mat-option value="header">Cabeçalho</mat-option>
3154
+ <mat-option value="window">Janela</mat-option>
3155
+ </mat-select>
3156
+ </mat-form-field>
3157
+ <mat-form-field appearance="outline">
3158
+ <mat-label>Emit</mat-label>
3159
+ <input matInput formControlName="emit" placeholder="ex: editar" />
3160
+ </mat-form-field>
3161
+ <mat-form-field appearance="outline">
3162
+ <mat-label>Tooltip</mat-label>
3163
+ <input matInput formControlName="tooltip" />
3164
+ </mat-form-field>
3165
+ </div>
3166
+ <div class="shell-action-row">
3167
+ <mat-form-field appearance="outline">
3168
+ <mat-label>Command</mat-label>
3169
+ <input matInput formControlName="command" placeholder="ex: add-item" />
3170
+ </mat-form-field>
3171
+ <mat-form-field appearance="outline">
3172
+ <mat-label>Payload (JSON)</mat-label>
3173
+ <textarea matInput rows="2" formControlName="payload" placeholder='ex: {"foo":"bar"}'></textarea>
3174
+ </mat-form-field>
3175
+ <mat-checkbox formControlName="visible">Visível</mat-checkbox>
3176
+ <mat-checkbox formControlName="disabled">Desabilitado</mat-checkbox>
3177
+ </div>
3178
+ @if (showActionInputPath(action.value)) {
3179
+ <div class="shell-action-hint">
3180
+ Input: {{ displayInputPath(extractActionInput(action.value)) }}
3181
+ </div>
3182
+ }
3183
+ <div class="shell-action-actions">
3184
+ <button mat-stroked-button color="warn" type="button" (click)="removeAction(i)">Remover</button>
3185
+ </div>
3186
+ </div>
3187
+ }
3188
+ </div>
3189
+ } @else {
3190
+ <div class="shell-empty">Nenhuma ação configurada.</div>
3191
+ }
3192
+
3193
+ </div>
3194
+ `, isInline: true, styles: [".shell-editor{display:grid;gap:20px;padding:12px 4px 20px}.shell-intro{margin:0;max-width:68ch;color:var(--md-sys-color-on-surface-variant);font-size:14px;line-height:1.5}.shell-section{margin:4px 0 -8px;color:var(--md-sys-color-on-surface);font-size:14px;font-weight:650;line-height:1.4}.shell-grid{display:grid;gap:12px 16px;grid-template-columns:repeat(2,minmax(0,1fr));align-items:start}.shell-field--wide{grid-column:1 / -1}.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}@media(max-width:640px){.shell-editor{padding-inline:0}.shell-grid{grid-template-columns:minmax(0,1fr)}.shell-field--wide{grid-column:auto}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.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: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i5$1.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: i6.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.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$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3195
+ }
3196
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: WidgetShellEditorComponent, decorators: [{
3197
+ type: Component,
3198
+ args: [{ selector: 'praxis-widget-shell-editor', standalone: true, imports: [
3199
+ CommonModule,
3200
+ FormsModule,
3201
+ ReactiveFormsModule,
3202
+ MatButtonModule,
3203
+ MatCheckboxModule,
3204
+ MatDividerModule,
3205
+ MatFormFieldModule,
3206
+ MatIconModule,
3207
+ MatInputModule,
3208
+ MatSelectModule,
3209
+ PraxisIconButtonComponent,
3210
+ PraxisIconDirective,
3211
+ ], template: `
3212
+ <div class="shell-editor">
3213
+ <p class="shell-intro">
3214
+ Defina a identidade, a aparência e as ações do card. Use a prévia para conferir o resultado antes de salvar.
3215
+ </p>
3216
+
3217
+ <div class="shell-section">Geral</div>
3218
+ <div class="shell-grid">
3219
+ <mat-form-field appearance="outline">
3220
+ <mat-label>Tipo de card</mat-label>
3221
+ <mat-select [formControl]="form.controls.kind">
3222
+ <mat-option value="dashboard-card">Dashboard Card</mat-option>
3223
+ <mat-option value="none">Sem shell</mat-option>
3224
+ </mat-select>
3225
+ </mat-form-field>
3226
+ <mat-form-field appearance="outline">
3227
+ <mat-label>Preset</mat-label>
3228
+ <mat-select [formControl]="form.controls.preset">
3229
+ <mat-option value="">Nenhum</mat-option>
3230
+ @if (pagePreset) {
3231
+ <mat-option [value]="pagePreset">Usar preset da página ({{ pagePreset }})</mat-option>
3232
+ }
3233
+ @for (p of presetOptions; track p) {
3234
+ <mat-option [value]="p.id">{{ p.label }}</mat-option>
3235
+ }
3236
+ </mat-select>
3237
+ </mat-form-field>
3238
+ @if (!form.controls.preset.value && pagePreset) {
3239
+ <div class="shell-hint">
3240
+ Preset da página: {{ pagePreset }}
3241
+ </div>
3242
+ }
3243
+ @if (form.controls.preset.value) {
3244
+ <mat-checkbox [formControl]="form.controls.applyToAll">
3245
+ Aplicar preset como padrão da página
3246
+ </mat-checkbox>
3247
+ }
3248
+ <mat-form-field appearance="outline">
3249
+ <mat-label>Ícone</mat-label>
3250
+ <input matInput [formControl]="form.controls.icon" placeholder="ex: badge" />
3251
+ </mat-form-field>
3252
+ <mat-form-field appearance="outline">
3253
+ <mat-label>Título</mat-label>
3254
+ <input matInput [formControl]="form.controls.title" />
3255
+ </mat-form-field>
3256
+ <mat-form-field appearance="outline" class="shell-field--wide">
3257
+ <mat-label>Subtítulo</mat-label>
3258
+ <input matInput [formControl]="form.controls.subtitle" />
3259
+ </mat-form-field>
3260
+ <mat-form-field appearance="outline">
3261
+ <mat-label>Mostrar cabeçalho</mat-label>
3262
+ <mat-select [formControl]="form.controls.showHeader">
3263
+ <mat-option value="auto">Automático</mat-option>
3264
+ <mat-option value="true">Sempre</mat-option>
3265
+ <mat-option value="false">Nunca</mat-option>
3266
+ </mat-select>
3267
+ </mat-form-field>
3268
+ </div>
3269
+
3270
+ <div class="shell-section">Controles de janela</div>
3271
+ <div class="shell-flags">
3272
+ <mat-checkbox [formControl]="form.controls.collapsible">Permitir recolher</mat-checkbox>
3273
+ </div>
3274
+
3275
+ <mat-divider></mat-divider>
3276
+
3277
+ <div class="shell-section">Aparência</div>
3278
+ <div class="shell-grid">
3279
+ <mat-form-field appearance="outline">
3280
+ <mat-label>Fundo do card</mat-label>
3281
+ <input matInput [formControl]="form.controls.cardBg" placeholder="ex: #0b1220" />
3282
+ </mat-form-field>
3283
+ <mat-form-field appearance="outline">
3284
+ <mat-label>Cor da borda</mat-label>
3285
+ <input matInput [formControl]="form.controls.cardBorder" placeholder="ex: rgba(255,255,255,0.12)" />
3286
+ </mat-form-field>
3287
+ <mat-form-field appearance="outline">
3288
+ <mat-label>Raio da borda</mat-label>
3289
+ <input matInput [formControl]="form.controls.cardRadius" placeholder="ex: 14px" />
3290
+ </mat-form-field>
3291
+ <mat-form-field appearance="outline">
3292
+ <mat-label>Sombra</mat-label>
3293
+ <input matInput [formControl]="form.controls.cardShadow" placeholder="ex: 0 10px 30px rgba(0,0,0,0.2)" />
3294
+ </mat-form-field>
3295
+ <mat-form-field appearance="outline">
3296
+ <mat-label>Fundo do cabeçalho</mat-label>
3297
+ <input matInput [formControl]="form.controls.headerBg" placeholder="ex: linear-gradient(...)" />
3298
+ </mat-form-field>
3299
+ <mat-form-field appearance="outline">
3300
+ <mat-label>Borda do cabeçalho</mat-label>
3301
+ <input matInput [formControl]="form.controls.headerBorder" placeholder="ex: rgba(255,255,255,0.08)" />
3302
+ </mat-form-field>
3303
+ <mat-form-field appearance="outline">
3304
+ <mat-label>Cor do título</mat-label>
3305
+ <input matInput [formControl]="form.controls.titleColor" placeholder="ex: #e6edf3" />
3306
+ </mat-form-field>
3307
+ <mat-form-field appearance="outline">
3308
+ <mat-label>Cor do subtítulo</mat-label>
3309
+ <input matInput [formControl]="form.controls.subtitleColor" placeholder="ex: #9fb0c4" />
3310
+ </mat-form-field>
3311
+ <mat-form-field appearance="outline">
3312
+ <mat-label>Cor do ícone</mat-label>
3313
+ <input matInput [formControl]="form.controls.iconColor" placeholder="ex: #8ab4ff" />
3314
+ </mat-form-field>
3315
+ <mat-form-field appearance="outline">
3316
+ <mat-label>Fundo do corpo</mat-label>
3317
+ <input matInput [formControl]="form.controls.bodyBg" placeholder="ex: transparent" />
3318
+ </mat-form-field>
3319
+ <mat-form-field appearance="outline">
3320
+ <mat-label>Cor do texto do corpo</mat-label>
3321
+ <input matInput [formControl]="form.controls.bodyColor" placeholder="ex: #e6edf3" />
3322
+ </mat-form-field>
3323
+ <mat-form-field appearance="outline">
3324
+ <mat-label>Padding do corpo</mat-label>
3325
+ <input matInput [formControl]="form.controls.bodyPadding" placeholder="ex: 10px 12px 12px" />
3326
+ </mat-form-field>
3327
+ <mat-form-field appearance="outline">
3328
+ <mat-label>Tamanho do título</mat-label>
3329
+ <input matInput [formControl]="form.controls.titleSize" placeholder="ex: 14px" />
3330
+ </mat-form-field>
3331
+ <mat-form-field appearance="outline">
3332
+ <mat-label>Peso do título</mat-label>
3333
+ <input matInput [formControl]="form.controls.titleWeight" placeholder="ex: 600" />
3334
+ </mat-form-field>
3335
+ <mat-form-field appearance="outline">
3336
+ <mat-label>Tamanho do subtítulo</mat-label>
3337
+ <input matInput [formControl]="form.controls.subtitleSize" placeholder="ex: 12px" />
3338
+ </mat-form-field>
3339
+ </div>
3340
+
3341
+ <div class="shell-preview" [ngStyle]="previewVars()">
3342
+ <div class="shell-preview-card" [class.no-shell]="form.controls.kind.value === 'none'" [class.expanded]="previewExpanded">
3343
+ @if (previewShowHeader()) {
3344
+ <div class="shell-preview-header">
3345
+ <div class="shell-preview-title">
3346
+ @if (form.controls.icon.value) {
3347
+ <mat-icon [praxisIcon]="form.controls.icon.value"></mat-icon>
3348
+ }
3349
+ <div class="shell-preview-text">
3350
+ <div class="shell-preview-title-text">{{ previewTitle() }}</div>
3351
+ <div class="shell-preview-subtitle">{{ previewSubtitle() }}</div>
3352
+ </div>
3353
+ </div>
3354
+ <div class="shell-preview-actions">
3355
+ @for (action of previewHeaderActions(); track action.id) {
3356
+ <button
3357
+ type="button"
3358
+ [disabled]="action.disabled"
3359
+ [ngClass]="action.variant === 'outlined' ? 'pdx-preview-outlined' : 'pdx-preview-text'"
3360
+ mat-button
3361
+ >
3362
+ @if (action.icon) {
3363
+ <mat-icon [praxisIcon]="action.icon"></mat-icon>
3364
+ }
3365
+ <span class="pdx-preview-label">{{ action.label || action.id }}</span>
3366
+ </button>
3367
+ }
3368
+ @if (previewOverflowCount()) {
3369
+ <button
3370
+ praxisIconButton="more_horiz"
3371
+ size="compact"
3372
+ type="button"
3373
+ aria-label="Mais ações"
3374
+ ></button>
3375
+ }
3376
+ @for (action of previewWindowActions(); track action.id) {
3377
+ @if (action.icon) {
3378
+ <button
3379
+ [praxisIconButton]="action.icon"
3380
+ size="compact"
3381
+ type="button"
3382
+ [disabled]="action.disabled"
3383
+ [attr.aria-label]="action.label || action.id"
3384
+ ></button>
3385
+ }
3386
+ }
3387
+ <button
3388
+ [praxisIconButton]="previewCollapsed ? 'unfold_more' : 'unfold_less'"
3389
+ size="compact"
3390
+ type="button"
3391
+ (click)="togglePreviewCollapse()"
3392
+ aria-label="Recolher"
3393
+ ></button>
3394
+ <button
3395
+ [praxisIconButton]="previewExpanded ? 'close_fullscreen' : 'open_in_full'"
3396
+ size="compact"
3397
+ type="button"
3398
+ (click)="togglePreviewExpand()"
3399
+ aria-label="Expandir"
3400
+ ></button>
3401
+ </div>
3402
+ </div>
3403
+ }
3404
+ <div class="shell-preview-body" [class.hidden]="previewCollapsed">Conteudo do widget</div>
3405
+ </div>
3406
+ </div>
3407
+
3408
+ <mat-divider></mat-divider>
3409
+
3410
+ @if (hasAvailableActions) {
3411
+ <div class="shell-section">Ações disponíveis</div>
3412
+ }
3413
+ @if (hasAvailableActions) {
3414
+ <div class="shell-available">
3415
+ <div class="shell-available-toolbar">
3416
+ <mat-form-field appearance="outline">
3417
+ <mat-label>Buscar ação</mat-label>
3418
+ <input matInput [formControl]="actionQuery" placeholder="ex: export, editar, submit" />
3419
+ </mat-form-field>
3420
+ <div class="shell-available-filters">
3421
+ <mat-checkbox [(ngModel)]="showScopeShell">Shell</mat-checkbox>
3422
+ <mat-checkbox [(ngModel)]="showScopeAny">Any</mat-checkbox>
3423
+ <mat-checkbox [(ngModel)]="showScopeToolbar">Toolbar</mat-checkbox>
3424
+ <mat-checkbox [(ngModel)]="showScopeContext">Context</mat-checkbox>
3425
+ </div>
3426
+ </div>
3427
+ <div class="shell-available-list">
3428
+ @for (action of filteredAvailableActions(); track action) {
3429
+ <div class="shell-available-item">
3430
+ <div class="shell-available-main">
3431
+ <div class="shell-available-title">
3432
+ @if (action.icon) {
3433
+ <mat-icon [praxisIcon]="action.icon"></mat-icon>
3434
+ }
3435
+ <div class="shell-available-text">
3436
+ <div class="shell-available-label">{{ action.label }}</div>
3437
+ <div class="shell-available-meta">
3438
+ <span class="shell-badge">{{ action.kind === 'command' ? 'Comando' : 'Ação' }}</span>
3439
+ <span class="shell-badge">{{ scopeLabel(action.scope) }}</span>
3440
+ <span class="shell-code">id: {{ action.id }}</span>
3441
+ @if (action.emit) {
3442
+ <span class="shell-code">emit: {{ action.emit }}</span>
3443
+ }
3444
+ @if (action.command) {
3445
+ <span class="shell-code">command: {{ action.command }}</span>
3446
+ }
3447
+ </div>
3448
+ </div>
3449
+ </div>
3450
+ @if (action.description) {
3451
+ <div class="shell-available-desc">{{ action.description }}</div>
3452
+ }
3453
+ @if (action.payloadExample !== undefined) {
3454
+ <div class="shell-available-payload">
3455
+ <span class="shell-code">payload: {{ stringifyPayload(action.payloadExample) }}</span>
3456
+ </div>
3457
+ }
3458
+ @if (action.scope === 'context') {
3459
+ <div class="shell-available-warning">
3460
+ Essa ação depende de item/linha selecionado; payload fixo pode não funcionar.
3461
+ </div>
3462
+ }
3463
+ </div>
3464
+ <div class="shell-available-actions">
3465
+ <button
3466
+ mat-stroked-button
3467
+ type="button"
3468
+ [color]="isActionSelected(action) ? 'warn' : 'primary'"
3469
+ (click)="toggleAvailableAction(action)"
3470
+ >
3471
+ {{ isActionSelected(action) ? 'Remover' : 'Adicionar' }}
3472
+ </button>
3473
+ </div>
3474
+ </div>
3475
+ }
3476
+ </div>
3477
+ </div>
3478
+ } @else {
3479
+ <div class="shell-empty">Nenhuma ação registrada para este componente.</div>
3480
+ }
3481
+
3482
+ @if (hasAvailableActions) {
3483
+ <mat-divider></mat-divider>
3484
+ }
3485
+
3486
+ @if (setInputPresets.length) {
3487
+ <div class="shell-section">Ações rápidas (inputs)</div>
3488
+ }
3489
+ @if (setInputPresets.length) {
3490
+ <div class="shell-quick-inputs">
3491
+ <mat-form-field appearance="outline">
3492
+ <mat-label>Input</mat-label>
3493
+ <mat-select [formControl]="setInputTarget">
3494
+ @for (preset of setInputPresets; track preset) {
3495
+ <mat-option [value]="preset.input">
3496
+ @if (preset.icon) {
3497
+ <mat-icon [praxisIcon]="preset.icon"></mat-icon>
3498
+ }
3499
+ {{ preset.label }}
3500
+ </mat-option>
3501
+ }
3502
+ </mat-select>
3503
+ </mat-form-field>
3504
+ @if (setInputTarget.value) {
3505
+ <div class="shell-input-path">
3506
+ Input: {{ displayInputPath(setInputTarget.value) }}
3507
+ </div>
3508
+ }
3509
+ <mat-form-field appearance="outline">
3510
+ <mat-label>Valor</mat-label>
3511
+ <mat-select [formControl]="setInputValue">
3512
+ @for (option of currentSetInputValues(); track option) {
3513
+ <mat-option [value]="option.value">
3514
+ @if (option.icon) {
3515
+ <mat-icon [praxisIcon]="option.icon"></mat-icon>
3516
+ }
3517
+ {{ option.label }}
3518
+ </mat-option>
3519
+ }
3520
+ </mat-select>
3521
+ </mat-form-field>
3522
+ <button mat-flat-button color="primary" type="button" (click)="addSetInputAction()">
3523
+ Adicionar ação
3524
+ </button>
3525
+ </div>
3526
+ }
3527
+
3528
+ <div class="shell-section shell-actions-head">
3529
+ <div>Ações do cabeçalho</div>
3530
+ <button mat-flat-button color="primary" type="button" (click)="addAction()">Adicionar ação</button>
3531
+ </div>
3532
+
3533
+ @if (actions.controls.length) {
3534
+ <div class="shell-actions">
3535
+ @for (action of actions.controls; track action; let i = $index) {
3536
+ <div class="shell-action" [formGroup]="action">
3537
+ <div class="shell-action-row">
3538
+ <mat-form-field appearance="outline">
3539
+ <mat-label>ID</mat-label>
3540
+ <input matInput formControlName="id" />
3541
+ </mat-form-field>
3542
+ <mat-form-field appearance="outline">
3543
+ <mat-label>Label</mat-label>
3544
+ <input matInput formControlName="label" />
3545
+ </mat-form-field>
3546
+ <mat-form-field appearance="outline">
3547
+ <mat-label>Ícone</mat-label>
3548
+ <input matInput formControlName="icon" />
3549
+ </mat-form-field>
3550
+ </div>
3551
+ <div class="shell-action-row">
3552
+ <mat-form-field appearance="outline">
3553
+ <mat-label>Variante</mat-label>
3554
+ <mat-select formControlName="variant">
3555
+ <mat-option value="text">Texto</mat-option>
3556
+ <mat-option value="outlined">Outlined</mat-option>
3557
+ <mat-option value="icon">Ícone</mat-option>
3558
+ </mat-select>
3559
+ </mat-form-field>
3560
+ <mat-form-field appearance="outline">
3561
+ <mat-label>Posição</mat-label>
3562
+ <mat-select formControlName="placement">
3563
+ <mat-option value="header">Cabeçalho</mat-option>
3564
+ <mat-option value="window">Janela</mat-option>
3565
+ </mat-select>
3566
+ </mat-form-field>
3567
+ <mat-form-field appearance="outline">
3568
+ <mat-label>Emit</mat-label>
3569
+ <input matInput formControlName="emit" placeholder="ex: editar" />
3570
+ </mat-form-field>
3571
+ <mat-form-field appearance="outline">
3572
+ <mat-label>Tooltip</mat-label>
3573
+ <input matInput formControlName="tooltip" />
3574
+ </mat-form-field>
3575
+ </div>
3576
+ <div class="shell-action-row">
3577
+ <mat-form-field appearance="outline">
3578
+ <mat-label>Command</mat-label>
3579
+ <input matInput formControlName="command" placeholder="ex: add-item" />
3580
+ </mat-form-field>
3581
+ <mat-form-field appearance="outline">
3582
+ <mat-label>Payload (JSON)</mat-label>
3583
+ <textarea matInput rows="2" formControlName="payload" placeholder='ex: {"foo":"bar"}'></textarea>
3584
+ </mat-form-field>
3585
+ <mat-checkbox formControlName="visible">Visível</mat-checkbox>
3586
+ <mat-checkbox formControlName="disabled">Desabilitado</mat-checkbox>
3587
+ </div>
3588
+ @if (showActionInputPath(action.value)) {
3589
+ <div class="shell-action-hint">
3590
+ Input: {{ displayInputPath(extractActionInput(action.value)) }}
3591
+ </div>
3592
+ }
3593
+ <div class="shell-action-actions">
3594
+ <button mat-stroked-button color="warn" type="button" (click)="removeAction(i)">Remover</button>
3595
+ </div>
3596
+ </div>
3597
+ }
3598
+ </div>
3599
+ } @else {
3600
+ <div class="shell-empty">Nenhuma ação configurada.</div>
3601
+ }
3602
+
3603
+ </div>
3604
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".shell-editor{display:grid;gap:20px;padding:12px 4px 20px}.shell-intro{margin:0;max-width:68ch;color:var(--md-sys-color-on-surface-variant);font-size:14px;line-height:1.5}.shell-section{margin:4px 0 -8px;color:var(--md-sys-color-on-surface);font-size:14px;font-weight:650;line-height:1.4}.shell-grid{display:grid;gap:12px 16px;grid-template-columns:repeat(2,minmax(0,1fr));align-items:start}.shell-field--wide{grid-column:1 / -1}.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}@media(max-width:640px){.shell-editor{padding-inline:0}.shell-grid{grid-template-columns:minmax(0,1fr)}.shell-field--wide{grid-column:auto}}\n"] }]
3605
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
3606
+ type: Optional
3607
+ }, {
3608
+ type: Inject,
3609
+ args: [SETTINGS_PANEL_DATA]
3610
+ }] }, { type: i2$2.ComponentMetadataRegistry, decorators: [{
3611
+ type: Optional
3612
+ }] }] });
3613
+
3614
+ function buildConnectionsViewerModel(page) {
3615
+ const widgets = page?.widgets ?? [];
3616
+ const links = page?.composition?.links ?? [];
3617
+ const counts = new Map();
3618
+ for (const widget of widgets) {
3619
+ counts.set(widget.key, { incoming: 0, outgoing: 0 });
3628
3620
  }
3621
+ const edges = links.map((link) => {
3622
+ const fromWidgetId = link.from.kind === 'component-port' ? link.from.ref.widget : null;
3623
+ const toWidgetId = link.to.kind === 'component-port' ? link.to.ref.widget : null;
3624
+ const fromNestedPath = link.from.kind === 'component-port' && link.from.ref.nestedPath?.length
3625
+ ? clone$2(link.from.ref.nestedPath)
3626
+ : null;
3627
+ const toNestedPath = link.to.kind === 'component-port' && link.to.ref.nestedPath?.length
3628
+ ? clone$2(link.to.ref.nestedPath)
3629
+ : null;
3630
+ const toStatePath = link.to.kind === 'state' ? link.to.ref.path : null;
3631
+ const diagnosticReasons = [];
3632
+ if (fromWidgetId && !counts.has(fromWidgetId)) {
3633
+ diagnosticReasons.push('missing-source-widget');
3634
+ }
3635
+ if (toWidgetId && !counts.has(toWidgetId)) {
3636
+ diagnosticReasons.push('missing-target-widget');
3637
+ }
3638
+ if (fromWidgetId) {
3639
+ const current = counts.get(fromWidgetId) ?? { incoming: 0, outgoing: 0 };
3640
+ current.outgoing += 1;
3641
+ counts.set(fromWidgetId, current);
3642
+ }
3643
+ if (toWidgetId) {
3644
+ const current = counts.get(toWidgetId) ?? { incoming: 0, outgoing: 0 };
3645
+ current.incoming += 1;
3646
+ counts.set(toWidgetId, current);
3647
+ }
3648
+ return {
3649
+ id: link.id,
3650
+ fromKind: link.from.kind,
3651
+ fromWidgetId,
3652
+ fromEndpointId: link.from.kind === 'component-port'
3653
+ ? buildComponentEndpointId(link.from.ref.widget, link.from.ref.nestedPath)
3654
+ : null,
3655
+ fromNestedPath,
3656
+ fromPort: link.from.kind === 'component-port' ? link.from.ref.port : null,
3657
+ toKind: link.to.kind,
3658
+ toWidgetId,
3659
+ toEndpointId: link.to.kind === 'component-port'
3660
+ ? buildComponentEndpointId(link.to.ref.widget, link.to.ref.nestedPath)
3661
+ : null,
3662
+ toNestedPath,
3663
+ toPort: link.to.kind === 'component-port' ? link.to.ref.port : null,
3664
+ toStatePath,
3665
+ intent: link.intent,
3666
+ hasCondition: !!link.condition,
3667
+ hasTransform: !!link.transform?.steps?.length,
3668
+ hasPolicy: !!link.policy && Object.keys(link.policy).length > 0,
3669
+ hasDiagnostics: diagnosticReasons.length > 0,
3670
+ diagnosticReasons,
3671
+ link,
3672
+ };
3673
+ });
3674
+ const nodes = widgets.map((widget) => {
3675
+ const counter = counts.get(widget.key) ?? { incoming: 0, outgoing: 0 };
3676
+ return {
3677
+ widgetId: widget.key,
3678
+ componentType: widget.definition?.id ?? 'unknown',
3679
+ incomingCount: counter.incoming,
3680
+ outgoingCount: counter.outgoing,
3681
+ };
3682
+ });
3629
3683
  return {
3630
- locale: options.locale,
3631
- fallbackLocale: options.fallbackLocale ?? 'pt-BR',
3632
- dictionaries,
3684
+ nodes,
3685
+ edges,
3686
+ totalLinks: edges.length,
3687
+ conditionLinks: edges.filter((edge) => edge.hasCondition).length,
3688
+ transformLinks: edges.filter((edge) => edge.hasTransform).length,
3689
+ policyLinks: edges.filter((edge) => edge.hasPolicy).length,
3690
+ diagnosticLinks: edges.filter((edge) => edge.hasDiagnostics).length,
3633
3691
  };
3634
3692
  }
3635
- function providePraxisPageBuilderI18n(options = {}) {
3636
- return providePraxisI18n(createPraxisPageBuilderI18nConfig(options));
3693
+ function buildComponentEndpointId(ownerWidgetKey, nestedPath) {
3694
+ if (!nestedPath?.length) {
3695
+ return ownerWidgetKey;
3696
+ }
3697
+ return `${ownerWidgetKey}${nestedPath
3698
+ .map((segment) => `/${formatPathSegment$1(segment)}`)
3699
+ .join('')}`;
3637
3700
  }
3638
- function resolvePraxisPageBuilderText(i18n, key, fallback) {
3639
- const namespacedKey = key.startsWith('praxis.pageBuilder.')
3640
- ? key
3641
- : `praxis.pageBuilder.${key}`;
3642
- return i18n.t(namespacedKey, undefined, fallback) || fallback;
3701
+ function formatPathSegment$1(segment) {
3702
+ if (segment.kind === 'widget') {
3703
+ const identity = segment.key
3704
+ || segment.componentType
3705
+ || (typeof segment.index === 'number' ? String(segment.index) : 'unknown');
3706
+ return `widget:${identity}`;
3707
+ }
3708
+ return [
3709
+ segment.kind,
3710
+ segment.id || segment.key || segment.index,
3711
+ ].filter((part) => part !== undefined && part !== '').join(':');
3712
+ }
3713
+ function clone$2(value) {
3714
+ if (value == null || typeof value !== 'object')
3715
+ return value;
3716
+ return JSON.parse(JSON.stringify(value));
3643
3717
  }
3644
3718
 
3645
3719
  const CONNECTION_EDITOR_STATE_NODE_ID = '__page_state__';
@@ -20961,7 +21035,7 @@ class DynamicPageBuilderComponent {
20961
21035
  }
20962
21036
  runtimePage() {
20963
21037
  const page = this.currentPage();
20964
- if (!this.previewMode) {
21038
+ if (!this.previewMode && this.showSettings()) {
20965
21039
  if (this.runtimeAuthoringPageCache?.source === page) {
20966
21040
  return this.runtimeAuthoringPageCache.value;
20967
21041
  }
@@ -20992,10 +21066,13 @@ class DynamicPageBuilderComponent {
20992
21066
  return this.tx('builderMode.presentation', 'Presentation');
20993
21067
  }
20994
21068
  onRuntimePageChange(next) {
20995
- const cloned = this.clonePage(next);
20996
- this.currentPage.set(cloned);
20997
- this.clearSelectedWidgetIfMissing(cloned);
20998
- this.pageChange.emit(cloned);
21069
+ const canonical = this.toCanonicalPageFromAuthoringRuntime(next);
21070
+ if (JSON.stringify(canonical) === JSON.stringify(this.currentPage())) {
21071
+ return;
21072
+ }
21073
+ this.currentPage.set(canonical);
21074
+ this.clearSelectedWidgetIfMissing(canonical);
21075
+ this.pageChange.emit(canonical);
20999
21076
  }
21000
21077
  onRuntimeWidgetEvent(event) {
21001
21078
  this.widgetEvent.emit(event);
@@ -23713,11 +23790,48 @@ class DynamicPageBuilderComponent {
23713
23790
  });
23714
23791
  return cloned;
23715
23792
  }
23793
+ /**
23794
+ * Removes authoring-only inputs from a page emitted by the embedded runtime.
23795
+ *
23796
+ * The runtime must receive `enableCustomization=true` so nested components can
23797
+ * expose their editors, but that projection is not part of the authored page.
23798
+ * Restore the canonical value by widget key before publishing `pageChange` so
23799
+ * a presentation-only edit cannot accidentally persist authoring chrome.
23800
+ */
23801
+ toCanonicalPageFromAuthoringRuntime(page) {
23802
+ const canonicalByKey = new Map((this.currentPage().widgets || []).map((widget) => [widget.key, widget]));
23803
+ const cloned = this.clonePage(page);
23804
+ cloned.widgets = (cloned.widgets || []).map((widget) => {
23805
+ const metadata = this.componentMetadata.get(widget.definition?.id);
23806
+ if (!metadata?.inputs?.some((input) => input.name === 'enableCustomization')) {
23807
+ return widget;
23808
+ }
23809
+ const canonicalInputs = canonicalByKey.get(widget.key)?.definition?.inputs;
23810
+ const inputs = { ...(widget.definition?.inputs || {}) };
23811
+ if (canonicalInputs && Object.prototype.hasOwnProperty.call(canonicalInputs, 'enableCustomization')) {
23812
+ inputs['enableCustomization'] = canonicalInputs['enableCustomization'];
23813
+ }
23814
+ else {
23815
+ delete inputs['enableCustomization'];
23816
+ }
23817
+ return {
23818
+ ...widget,
23819
+ definition: {
23820
+ ...widget.definition,
23821
+ inputs,
23822
+ },
23823
+ };
23824
+ });
23825
+ return cloned;
23826
+ }
23716
23827
  toPresentationRuntimeWidget(widget) {
23717
- const inputs = widget.definition?.inputs;
23718
- if (!inputs || inputs['enableCustomization'] !== true) {
23828
+ const metadata = this.componentMetadata.get(widget.definition?.id);
23829
+ if (!metadata?.inputs?.some((input) => input.name === 'enableCustomization')) {
23719
23830
  return widget;
23720
23831
  }
23832
+ const inputs = widget.definition?.inputs || {};
23833
+ if (inputs['enableCustomization'] === false)
23834
+ return widget;
23721
23835
  return {
23722
23836
  ...widget,
23723
23837
  definition: {
@@ -24959,6 +25073,11 @@ class DynamicPageBuilderComponent {
24959
25073
  [previewActive]="isPreviewMode()"
24960
25074
  [connectionMode]="showSettings() && connectionsViewerOpen()"
24961
25075
  [modeLabel]="builderModeLabel()"
25076
+ [addLabel]="tx('toolbar.add', 'Inserir componente')"
25077
+ [saveLabel]="tx('toolbar.save', 'Salvar página')"
25078
+ [undoLabel]="tx('toolbar.undo', 'Desfazer')"
25079
+ [redoLabel]="tx('toolbar.redo', 'Refazer')"
25080
+ [settingsLabel]="tx('toolbar.settings', 'Configurações da página')"
24962
25081
  [previewLabel]="tx('toolbar.preview', 'Pré-visualizar')"
24963
25082
  [exitPreviewLabel]="tx('toolbar.exitPreview', 'Voltar para edição')"
24964
25083
  (add)="onAddComponent()"
@@ -25036,7 +25155,7 @@ class DynamicPageBuilderComponent {
25036
25155
  }
25037
25156
  </praxis-floating-toolbar>
25038
25157
  </div>
25039
- `, isInline: true, styles: [":host{display:block;position:relative;min-height:var(--pdx-page-builder-min-height, 420px)}.builder-shell{display:block;position:relative;min-height:inherit;height:100%;box-sizing:border-box}.builder-shell--bottom-toolbar{padding-block-end:calc(80px + env(safe-area-inset-bottom,0px))}.builder-shell:has(.pdx-shell.fullscreen) praxis-floating-toolbar{display:none}.builder-premium-readiness{display:grid;grid-template-columns:minmax(220px,.72fr) minmax(0,1.28fr);gap:12px;width:100%;margin:0 0 14px;padding:14px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #cac4d0) 72%,transparent);border-radius:12px;background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-primary, #6750a4) 10%,transparent),transparent 36%),color-mix(in srgb,var(--md-sys-color-surface-container-lowest, #fff) 96%,transparent);color:var(--md-sys-color-on-surface, #1d1b20);box-shadow:inset 0 1px color-mix(in srgb,var(--md-sys-color-on-surface, #1d1b20) 5%,transparent)}.builder-premium-readiness__header{display:flex;align-items:flex-start;gap:10px;min-width:0}.builder-premium-readiness__header strong{display:block;font-size:13px;line-height:1.25}.builder-premium-readiness__header p,.builder-premium-readiness__item p{margin:2px 0 0;color:var(--md-sys-color-on-surface-variant, #49454f);font-size:11px;line-height:1.35}.builder-premium-readiness__icon{color:var(--md-sys-color-primary, #6750a4);font-size:20px;line-height:1}.builder-premium-readiness__list{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:8px;margin:0;padding:0;list-style:none}.builder-premium-readiness__item{display:grid;grid-template-columns:auto minmax(0,1fr);gap:8px;min-width:0;padding:9px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #cac4d0) 64%,transparent);border-radius:10px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low, #f7f2fa) 84%,transparent)}.builder-premium-readiness__item strong{display:block;font-size:12px;line-height:1.25}.builder-premium-readiness__action{appearance:none;display:inline-flex;align-items:center;justify-content:center;min-height:26px;margin-top:7px;padding:4px 8px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary, #6750a4) 34%,transparent);border-radius:7px;background:color-mix(in srgb,var(--md-sys-color-primary, #6750a4) 9%,var(--md-sys-color-surface, #fff));color:var(--md-sys-color-primary, #6750a4);cursor:pointer;font:inherit;font-size:11px;font-weight:700;line-height:1.2;text-align:left}.builder-premium-readiness__severity{width:8px;height:8px;margin-top:4px;border-radius:999px;background:var(--md-sys-color-primary, #6750a4)}.builder-premium-readiness__item[data-severity=warning] .builder-premium-readiness__severity{background:var(--praxis-warning, #b26a00)}.builder-premium-readiness__item[data-severity=error] .builder-premium-readiness__severity{background:var(--md-sys-color-error, #b3261e)}@media(max-width:760px){.builder-premium-readiness{grid-template-columns:1fr}}.agentic-diagnostics-panel{position:absolute;z-index:21;right:16px;width:min(520px,calc(100% - 32px));max-height:min(440px,calc(100% - 32px));overflow:auto;padding:12px;border:1px solid rgba(107,114,128,.28);border-radius:8px;background:#fffffff5;color:#111827;box-shadow:0 14px 36px #0000003d}.agentic-diagnostics-panel--collapsed{width:min(280px,calc(100% - 32px));max-height:58px;overflow:hidden;padding:10px 12px;pointer-events:auto}.agentic-diagnostics-panel__header{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:13px;font-weight:700}.agentic-diagnostics-panel__header-actions{display:flex;align-items:center;gap:8px}.agentic-diagnostics-panel__badge{padding:2px 6px;border:1px solid rgba(107,114,128,.28);border-radius:8px;font-size:11px;font-weight:600;color:#4b5563}.agentic-diagnostics-panel__toggle,.shared-rule-cockpit__toggle{appearance:none;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 18%,transparent);border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-surface-container-lowest) 84%,transparent);color:var(--md-sys-color-primary);cursor:pointer;font-size:11px;font-weight:700;line-height:1;padding:6px 9px;white-space:nowrap}.agentic-diagnostics-panel__description{margin:8px 0 10px;color:#4b5563;font-size:12px;line-height:1.4}.agentic-diagnostics-panel pre{margin:0;white-space:pre-wrap;overflow-wrap:anywhere;font-size:11px;line-height:1.45;color:#1f2937}.agentic-diagnostics-panel__health{display:grid;gap:8px;margin-bottom:12px}.agentic-diagnostics-panel__health h3,.agentic-diagnostics-panel__health p{margin:0}.agentic-diagnostics-panel__health h3{font-size:13px}.agentic-diagnostics-panel__health p{color:#4b5563;font-size:11px}.agentic-diagnostics-panel__metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:0}.agentic-diagnostics-panel__metrics div{padding:8px;border-radius:8px;background:#f3f4f6}.agentic-diagnostics-panel__metrics dt{color:#4b5563;font-size:10px}.agentic-diagnostics-panel__metrics dd{margin:3px 0 0;font-size:14px;font-weight:700}.agentic-diagnostics-panel__health-table{width:100%;border-collapse:collapse;font-size:10px}.agentic-diagnostics-panel__health-table caption{margin-bottom:4px;text-align:left;font-weight:700}.agentic-diagnostics-panel__health-table th,.agentic-diagnostics-panel__health-table td{padding:5px 4px;border-bottom:1px solid #e5e7eb;text-align:left}.agentic-diagnostics-panel__raw summary{margin-bottom:8px;cursor:pointer;font-size:11px;font-weight:700}@media(max-width:520px){.agentic-diagnostics-panel__metrics{grid-template-columns:repeat(2,minmax(0,1fr))}}praxis-ai-assistant-shell{position:fixed;inset:0;z-index:140;pointer-events:none}@media(min-width:1100px){.builder-shell--agentic-review-rail{box-sizing:border-box;padding-right:464px}}.builder-shell__agentic-toggle{position:relative}.builder-shell__agentic-toggle--open,.builder-shell__agentic-toggle--minimized{outline:2px solid rgba(96,165,250,.52);outline-offset:2px}.builder-shell__agentic-toggle--minimized:after{content:\"\";position:absolute;top:6px;right:6px;width:8px;height:8px;border:2px solid var(--md-sys-color-surface, #111827);border-radius:999px;background:#34d399}.builder-shell__connections-toggle--open{outline:2px solid color-mix(in srgb,var(--md-sys-color-primary, #6750a4) 52%,transparent);outline-offset:2px}.builder-shell__connections-toggle{position:relative}.builder-shell__connections-toggle--recommended:not(.builder-shell__connections-toggle--open){outline:2px solid rgba(34,211,238,.48);outline-offset:2px}.builder-shell__connections-toggle-badge{position:absolute;top:5px;right:5px;width:9px;height:9px;border:2px solid var(--md-sys-color-surface, #111827);border-radius:999px;background:#22d3ee;box-shadow:0 0 0 3px #22d3ee2e}.shared-rule-cockpit{position:absolute;z-index:120;left:16px;bottom:88px;width:min(720px,calc(100% - 32px));max-height:min(560px,calc(100vh - 140px));padding:14px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 24%,transparent);border-radius:12px;background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-primary-container) 24%,var(--md-sys-color-surface-container-high) 76%),color-mix(in srgb,var(--md-sys-color-tertiary-container) 18%,var(--md-sys-color-surface-container) 82%));color:var(--md-sys-color-on-surface);box-shadow:0 18px 46px color-mix(in srgb,var(--md-sys-color-shadow, #000) 24%,transparent);overflow:auto;overscroll-behavior:contain}.shared-rule-cockpit--collapsed{width:min(360px,calc(100% - 32px));max-height:72px;overflow:hidden;padding:10px 12px}.shared-rule-cockpit__header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.shared-rule-cockpit__header-actions{display:flex;align-items:center;gap:8px}.shared-rule-cockpit__header div{display:grid;gap:2px}.shared-rule-cockpit__header strong{font-size:14px;line-height:1.2}.shared-rule-cockpit__header span{color:var(--md-sys-color-on-surface-variant);font-size:12px;overflow-wrap:anywhere}.shared-rule-cockpit__badge{flex:0 0 auto;max-width:220px;padding:3px 8px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 20%,transparent);border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-surface-container-lowest) 74%,transparent);color:var(--md-sys-color-primary);font-size:11px;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.shared-rule-cockpit__description,.shared-rule-cockpit__status,.shared-rule-cockpit__error{margin:10px 0 0;color:var(--md-sys-color-on-surface-variant);font-size:12px;line-height:1.45}.shared-rule-cockpit__error{color:var(--md-sys-color-error);font-weight:600}.shared-rule-cockpit__actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}.shared-rule-cockpit__facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin:12px 0 0}.shared-rule-cockpit__facts div{min-width:0;padding:8px;border-radius:10px;background:color-mix(in srgb,var(--md-sys-color-surface-container-lowest) 72%,transparent)}.shared-rule-cockpit__facts dt{color:var(--md-sys-color-on-surface-variant);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em}.shared-rule-cockpit__facts dd{margin:4px 0 0;color:var(--md-sys-color-on-surface);font-size:12px;overflow-wrap:anywhere}.shared-rule-cockpit__enforcement-matrix{display:grid;gap:10px;margin-top:12px;padding:10px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 14%,transparent);border-radius:12px;background:color-mix(in srgb,var(--md-sys-color-surface-container-lowest) 58%,transparent)}.shared-rule-cockpit__matrix-header{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}.shared-rule-cockpit__matrix-header div{display:grid;gap:2px}.shared-rule-cockpit__matrix-header strong{color:var(--md-sys-color-on-surface);font-size:12px}.shared-rule-cockpit__matrix-header span,.shared-rule-cockpit__matrix-source{color:var(--md-sys-color-on-surface-variant);font-size:11px;line-height:1.35;overflow-wrap:anywhere}.shared-rule-cockpit__matrix-badge{flex:0 0 auto;padding:3px 8px;border-radius:999px;background:#16a34a1f;color:#166534;font-weight:800;white-space:nowrap}.shared-rule-cockpit__projection-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.shared-rule-cockpit__projection{display:grid;gap:3px;min-width:0;padding:9px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 52%,transparent);border-radius:11px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 82%,transparent)}.shared-rule-cockpit__projection[data-status=ready]{border-color:color-mix(in srgb,var(--md-sys-color-tertiary) 24%,transparent);background:color-mix(in srgb,var(--md-sys-color-tertiary-container) 32%,var(--md-sys-color-surface-container-low) 68%)}.shared-rule-cockpit__projection span{color:var(--md-sys-color-primary);font-size:10px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;overflow-wrap:anywhere}.shared-rule-cockpit__projection strong{color:var(--md-sys-color-on-surface);font-size:12px}.shared-rule-cockpit__projection small,.shared-rule-cockpit__projection em{color:var(--md-sys-color-on-surface-variant);font-size:11px;font-style:normal;overflow-wrap:anywhere}.shared-rule-cockpit__matrix-source{margin:0}.shared-rule-cockpit__timeline{display:grid;gap:8px;max-height:190px;margin:12px 0 0;padding:0;overflow:auto;list-style:none}.shared-rule-cockpit__timeline-item{display:grid;grid-template-columns:auto minmax(0,1fr);gap:10px;padding:10px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 12%,transparent);border-radius:12px;background:color-mix(in srgb,var(--md-sys-color-surface-container-lowest) 76%,transparent)}.shared-rule-cockpit__timeline-icon{display:grid;width:24px;height:24px;place-items:center;border-radius:999px;background:#1e40af1a;color:var(--md-sys-color-primary);font-family:Material Icons;font-size:16px;line-height:1}.shared-rule-cockpit__timeline-item strong{color:var(--md-sys-color-on-surface);font-size:12px;line-height:1.35}.shared-rule-cockpit__timeline-item p,.shared-rule-cockpit__timeline-item small,.shared-rule-cockpit__timeline-item time,.shared-rule-cockpit__timeline-empty{display:block;margin:3px 0 0;color:var(--md-sys-color-on-surface-variant);font-size:11px;line-height:1.35;overflow-wrap:anywhere}.shared-rule-cockpit__timeline-badge{display:inline-flex;margin-left:6px;padding:2px 6px;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-secondary-container) 42%,transparent);color:var(--md-sys-color-on-secondary-container);font-size:10px;font-weight:700}.shared-rule-cockpit__timeline-empty{padding:10px;border-radius:12px;background:color-mix(in srgb,var(--md-sys-color-surface-container-lowest) 64%,transparent)}@media(max-width:720px){.shared-rule-cockpit{bottom:72px;max-height:calc(100vh - 112px)}.shared-rule-cockpit__facts,.shared-rule-cockpit__projection-grid{grid-template-columns:1fr}}\n"], dependencies: [{ 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.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: DynamicWidgetPageComponent, selector: "praxis-dynamic-page", inputs: ["page", "context", "strictValidation", "enableCustomization", "showPageSettingsButton", "shellEditorComponent", "pageEditorComponent", "autoPersist", "pageIdentity", "componentInstanceId", "showWidgetAssistantButton"], outputs: ["pageChange", "widgetEvent", "widgetSelectionChange", "widgetAssistantRequested", "widgetDiagnosticsChange"] }, { kind: "component", type: FloatingToolbarComponent, selector: "praxis-floating-toolbar", inputs: ["visible", "canUndo", "canRedo", "showSave", "showPreview", "editingEnabled", "previewActive", "connectionMode", "modeLabel", "previewLabel", "exitPreviewLabel"], outputs: ["add", "undo", "redo", "settings", "preview", "save"] }, { kind: "component", type: ConnectionEditorComponent, selector: "praxis-connection-editor", inputs: ["open", "page"], outputs: ["pageChange", "focusWidget", "openPageSettings", "close"] }, { kind: "component", type: PraxisAiAssistantShellComponent, selector: "praxis-ai-assistant-shell", inputs: ["labels", "mode", "state", "contextItems", "attachments", "messages", "quickReplies", "recommendedIntents", "prompt", "statusText", "errorText", "testIdPrefix", "panelTestId", "submitTestId", "applyTestId", "primaryAction", "secondaryActions", "governanceActions", "busy", "canSubmit", "canApply", "submitOnEnter", "showAttachAction", "enablePastedAttachments", "enableFileAttachments", "attachmentAccept", "attachmentMultiple", "voiceInputMode", "voiceLanguage", "draggable", "resizable", "minWidth", "minHeight", "margin", "layout"], outputs: ["promptChange", "submitPrompt", "apply", "retryTurn", "cancelTurn", "shellAction", "close", "attach", "attachmentsPasted", "attachmentsSelected", "removeAttachment", "messageAction", "editMessage", "resendMessage", "quickReply", "recommendedIntent", "layoutChange"] }] });
25158
+ `, isInline: true, styles: [":host{display:block;position:relative;min-height:var(--pdx-page-builder-min-height, 420px)}.builder-shell{display:block;position:relative;min-height:inherit;height:100%;box-sizing:border-box}.builder-shell--bottom-toolbar{padding-block-end:calc(80px + env(safe-area-inset-bottom,0px))}.builder-shell:has(.pdx-shell.fullscreen) praxis-floating-toolbar{display:none}.builder-premium-readiness{display:grid;grid-template-columns:minmax(220px,.72fr) minmax(0,1.28fr);gap:12px;width:100%;margin:0 0 14px;padding:14px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #cac4d0) 72%,transparent);border-radius:12px;background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-primary, #6750a4) 10%,transparent),transparent 36%),color-mix(in srgb,var(--md-sys-color-surface-container-lowest, #fff) 96%,transparent);color:var(--md-sys-color-on-surface, #1d1b20);box-shadow:inset 0 1px color-mix(in srgb,var(--md-sys-color-on-surface, #1d1b20) 5%,transparent)}.builder-premium-readiness__header{display:flex;align-items:flex-start;gap:10px;min-width:0}.builder-premium-readiness__header strong{display:block;font-size:13px;line-height:1.25}.builder-premium-readiness__header p,.builder-premium-readiness__item p{margin:2px 0 0;color:var(--md-sys-color-on-surface-variant, #49454f);font-size:11px;line-height:1.35}.builder-premium-readiness__icon{color:var(--md-sys-color-primary, #6750a4);font-size:20px;line-height:1}.builder-premium-readiness__list{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:8px;margin:0;padding:0;list-style:none}.builder-premium-readiness__item{display:grid;grid-template-columns:auto minmax(0,1fr);gap:8px;min-width:0;padding:9px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #cac4d0) 64%,transparent);border-radius:10px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low, #f7f2fa) 84%,transparent)}.builder-premium-readiness__item strong{display:block;font-size:12px;line-height:1.25}.builder-premium-readiness__action{appearance:none;display:inline-flex;align-items:center;justify-content:center;min-height:26px;margin-top:7px;padding:4px 8px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary, #6750a4) 34%,transparent);border-radius:7px;background:color-mix(in srgb,var(--md-sys-color-primary, #6750a4) 9%,var(--md-sys-color-surface, #fff));color:var(--md-sys-color-primary, #6750a4);cursor:pointer;font:inherit;font-size:11px;font-weight:700;line-height:1.2;text-align:left}.builder-premium-readiness__severity{width:8px;height:8px;margin-top:4px;border-radius:999px;background:var(--md-sys-color-primary, #6750a4)}.builder-premium-readiness__item[data-severity=warning] .builder-premium-readiness__severity{background:var(--praxis-warning, #b26a00)}.builder-premium-readiness__item[data-severity=error] .builder-premium-readiness__severity{background:var(--md-sys-color-error, #b3261e)}@media(max-width:760px){.builder-premium-readiness{grid-template-columns:1fr}}.agentic-diagnostics-panel{position:absolute;z-index:21;right:16px;width:min(520px,calc(100% - 32px));max-height:min(440px,calc(100% - 32px));overflow:auto;padding:12px;border:1px solid rgba(107,114,128,.28);border-radius:8px;background:#fffffff5;color:#111827;box-shadow:0 14px 36px #0000003d}.agentic-diagnostics-panel--collapsed{width:min(280px,calc(100% - 32px));max-height:58px;overflow:hidden;padding:10px 12px;pointer-events:auto}.agentic-diagnostics-panel__header{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:13px;font-weight:700}.agentic-diagnostics-panel__header-actions{display:flex;align-items:center;gap:8px}.agentic-diagnostics-panel__badge{padding:2px 6px;border:1px solid rgba(107,114,128,.28);border-radius:8px;font-size:11px;font-weight:600;color:#4b5563}.agentic-diagnostics-panel__toggle,.shared-rule-cockpit__toggle{appearance:none;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 18%,transparent);border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-surface-container-lowest) 84%,transparent);color:var(--md-sys-color-primary);cursor:pointer;font-size:11px;font-weight:700;line-height:1;padding:6px 9px;white-space:nowrap}.agentic-diagnostics-panel__description{margin:8px 0 10px;color:#4b5563;font-size:12px;line-height:1.4}.agentic-diagnostics-panel pre{margin:0;white-space:pre-wrap;overflow-wrap:anywhere;font-size:11px;line-height:1.45;color:#1f2937}.agentic-diagnostics-panel__health{display:grid;gap:8px;margin-bottom:12px}.agentic-diagnostics-panel__health h3,.agentic-diagnostics-panel__health p{margin:0}.agentic-diagnostics-panel__health h3{font-size:13px}.agentic-diagnostics-panel__health p{color:#4b5563;font-size:11px}.agentic-diagnostics-panel__metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:0}.agentic-diagnostics-panel__metrics div{padding:8px;border-radius:8px;background:#f3f4f6}.agentic-diagnostics-panel__metrics dt{color:#4b5563;font-size:10px}.agentic-diagnostics-panel__metrics dd{margin:3px 0 0;font-size:14px;font-weight:700}.agentic-diagnostics-panel__health-table{width:100%;border-collapse:collapse;font-size:10px}.agentic-diagnostics-panel__health-table caption{margin-bottom:4px;text-align:left;font-weight:700}.agentic-diagnostics-panel__health-table th,.agentic-diagnostics-panel__health-table td{padding:5px 4px;border-bottom:1px solid #e5e7eb;text-align:left}.agentic-diagnostics-panel__raw summary{margin-bottom:8px;cursor:pointer;font-size:11px;font-weight:700}@media(max-width:520px){.agentic-diagnostics-panel__metrics{grid-template-columns:repeat(2,minmax(0,1fr))}}praxis-ai-assistant-shell{position:fixed;inset:0;z-index:140;pointer-events:none}@media(min-width:1100px){.builder-shell--agentic-review-rail{box-sizing:border-box;padding-right:464px}}.builder-shell__agentic-toggle{position:relative}.builder-shell__agentic-toggle--open,.builder-shell__agentic-toggle--minimized{outline:2px solid rgba(96,165,250,.52);outline-offset:2px}.builder-shell__agentic-toggle--minimized:after{content:\"\";position:absolute;top:6px;right:6px;width:8px;height:8px;border:2px solid var(--md-sys-color-surface, #111827);border-radius:999px;background:#34d399}.builder-shell__connections-toggle--open{outline:2px solid color-mix(in srgb,var(--md-sys-color-primary, #6750a4) 52%,transparent);outline-offset:2px}.builder-shell__connections-toggle{position:relative}.builder-shell__connections-toggle--recommended:not(.builder-shell__connections-toggle--open){outline:2px solid rgba(34,211,238,.48);outline-offset:2px}.builder-shell__connections-toggle-badge{position:absolute;top:5px;right:5px;width:9px;height:9px;border:2px solid var(--md-sys-color-surface, #111827);border-radius:999px;background:#22d3ee;box-shadow:0 0 0 3px #22d3ee2e}.shared-rule-cockpit{position:absolute;z-index:120;left:16px;bottom:88px;width:min(720px,calc(100% - 32px));max-height:min(560px,calc(100vh - 140px));padding:14px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 24%,transparent);border-radius:12px;background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-primary-container) 24%,var(--md-sys-color-surface-container-high) 76%),color-mix(in srgb,var(--md-sys-color-tertiary-container) 18%,var(--md-sys-color-surface-container) 82%));color:var(--md-sys-color-on-surface);box-shadow:0 18px 46px color-mix(in srgb,var(--md-sys-color-shadow, #000) 24%,transparent);overflow:auto;overscroll-behavior:contain}.shared-rule-cockpit--collapsed{width:min(360px,calc(100% - 32px));max-height:72px;overflow:hidden;padding:10px 12px}.shared-rule-cockpit__header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.shared-rule-cockpit__header-actions{display:flex;align-items:center;gap:8px}.shared-rule-cockpit__header div{display:grid;gap:2px}.shared-rule-cockpit__header strong{font-size:14px;line-height:1.2}.shared-rule-cockpit__header span{color:var(--md-sys-color-on-surface-variant);font-size:12px;overflow-wrap:anywhere}.shared-rule-cockpit__badge{flex:0 0 auto;max-width:220px;padding:3px 8px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 20%,transparent);border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-surface-container-lowest) 74%,transparent);color:var(--md-sys-color-primary);font-size:11px;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.shared-rule-cockpit__description,.shared-rule-cockpit__status,.shared-rule-cockpit__error{margin:10px 0 0;color:var(--md-sys-color-on-surface-variant);font-size:12px;line-height:1.45}.shared-rule-cockpit__error{color:var(--md-sys-color-error);font-weight:600}.shared-rule-cockpit__actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}.shared-rule-cockpit__facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin:12px 0 0}.shared-rule-cockpit__facts div{min-width:0;padding:8px;border-radius:10px;background:color-mix(in srgb,var(--md-sys-color-surface-container-lowest) 72%,transparent)}.shared-rule-cockpit__facts dt{color:var(--md-sys-color-on-surface-variant);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em}.shared-rule-cockpit__facts dd{margin:4px 0 0;color:var(--md-sys-color-on-surface);font-size:12px;overflow-wrap:anywhere}.shared-rule-cockpit__enforcement-matrix{display:grid;gap:10px;margin-top:12px;padding:10px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 14%,transparent);border-radius:12px;background:color-mix(in srgb,var(--md-sys-color-surface-container-lowest) 58%,transparent)}.shared-rule-cockpit__matrix-header{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}.shared-rule-cockpit__matrix-header div{display:grid;gap:2px}.shared-rule-cockpit__matrix-header strong{color:var(--md-sys-color-on-surface);font-size:12px}.shared-rule-cockpit__matrix-header span,.shared-rule-cockpit__matrix-source{color:var(--md-sys-color-on-surface-variant);font-size:11px;line-height:1.35;overflow-wrap:anywhere}.shared-rule-cockpit__matrix-badge{flex:0 0 auto;padding:3px 8px;border-radius:999px;background:#16a34a1f;color:#166534;font-weight:800;white-space:nowrap}.shared-rule-cockpit__projection-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.shared-rule-cockpit__projection{display:grid;gap:3px;min-width:0;padding:9px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 52%,transparent);border-radius:11px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 82%,transparent)}.shared-rule-cockpit__projection[data-status=ready]{border-color:color-mix(in srgb,var(--md-sys-color-tertiary) 24%,transparent);background:color-mix(in srgb,var(--md-sys-color-tertiary-container) 32%,var(--md-sys-color-surface-container-low) 68%)}.shared-rule-cockpit__projection span{color:var(--md-sys-color-primary);font-size:10px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;overflow-wrap:anywhere}.shared-rule-cockpit__projection strong{color:var(--md-sys-color-on-surface);font-size:12px}.shared-rule-cockpit__projection small,.shared-rule-cockpit__projection em{color:var(--md-sys-color-on-surface-variant);font-size:11px;font-style:normal;overflow-wrap:anywhere}.shared-rule-cockpit__matrix-source{margin:0}.shared-rule-cockpit__timeline{display:grid;gap:8px;max-height:190px;margin:12px 0 0;padding:0;overflow:auto;list-style:none}.shared-rule-cockpit__timeline-item{display:grid;grid-template-columns:auto minmax(0,1fr);gap:10px;padding:10px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 12%,transparent);border-radius:12px;background:color-mix(in srgb,var(--md-sys-color-surface-container-lowest) 76%,transparent)}.shared-rule-cockpit__timeline-icon{display:grid;width:24px;height:24px;place-items:center;border-radius:999px;background:#1e40af1a;color:var(--md-sys-color-primary);font-family:Material Icons;font-size:16px;line-height:1}.shared-rule-cockpit__timeline-item strong{color:var(--md-sys-color-on-surface);font-size:12px;line-height:1.35}.shared-rule-cockpit__timeline-item p,.shared-rule-cockpit__timeline-item small,.shared-rule-cockpit__timeline-item time,.shared-rule-cockpit__timeline-empty{display:block;margin:3px 0 0;color:var(--md-sys-color-on-surface-variant);font-size:11px;line-height:1.35;overflow-wrap:anywhere}.shared-rule-cockpit__timeline-badge{display:inline-flex;margin-left:6px;padding:2px 6px;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-secondary-container) 42%,transparent);color:var(--md-sys-color-on-secondary-container);font-size:10px;font-weight:700}.shared-rule-cockpit__timeline-empty{padding:10px;border-radius:12px;background:color-mix(in srgb,var(--md-sys-color-surface-container-lowest) 64%,transparent)}@media(max-width:720px){.shared-rule-cockpit{bottom:72px;max-height:calc(100vh - 112px)}.shared-rule-cockpit__facts,.shared-rule-cockpit__projection-grid{grid-template-columns:1fr}}\n"], dependencies: [{ 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.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: DynamicWidgetPageComponent, selector: "praxis-dynamic-page", inputs: ["page", "context", "strictValidation", "enableCustomization", "showPageSettingsButton", "shellEditorComponent", "pageEditorComponent", "autoPersist", "pageIdentity", "componentInstanceId", "showWidgetAssistantButton"], outputs: ["pageChange", "widgetEvent", "widgetSelectionChange", "widgetAssistantRequested", "widgetDiagnosticsChange"] }, { kind: "component", type: FloatingToolbarComponent, selector: "praxis-floating-toolbar", inputs: ["visible", "canUndo", "canRedo", "showSave", "showPreview", "editingEnabled", "previewActive", "connectionMode", "modeLabel", "addLabel", "saveLabel", "undoLabel", "redoLabel", "settingsLabel", "previewLabel", "exitPreviewLabel"], outputs: ["add", "undo", "redo", "settings", "preview", "save"] }, { kind: "component", type: ConnectionEditorComponent, selector: "praxis-connection-editor", inputs: ["open", "page"], outputs: ["pageChange", "focusWidget", "openPageSettings", "close"] }, { kind: "component", type: PraxisAiAssistantShellComponent, selector: "praxis-ai-assistant-shell", inputs: ["labels", "mode", "state", "contextItems", "attachments", "messages", "quickReplies", "recommendedIntents", "prompt", "statusText", "errorText", "testIdPrefix", "panelTestId", "submitTestId", "applyTestId", "primaryAction", "secondaryActions", "governanceActions", "busy", "canSubmit", "canApply", "submitOnEnter", "showAttachAction", "enablePastedAttachments", "enableFileAttachments", "attachmentAccept", "attachmentMultiple", "voiceInputMode", "voiceLanguage", "draggable", "resizable", "minWidth", "minHeight", "margin", "layout"], outputs: ["promptChange", "submitPrompt", "apply", "retryTurn", "cancelTurn", "shellAction", "close", "attach", "attachmentsPasted", "attachmentsSelected", "removeAttachment", "messageAction", "editMessage", "resendMessage", "quickReply", "recommendedIntent", "layoutChange"] }] });
25040
25159
  }
25041
25160
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicPageBuilderComponent, decorators: [{
25042
25161
  type: Component,
@@ -25515,6 +25634,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
25515
25634
  [previewActive]="isPreviewMode()"
25516
25635
  [connectionMode]="showSettings() && connectionsViewerOpen()"
25517
25636
  [modeLabel]="builderModeLabel()"
25637
+ [addLabel]="tx('toolbar.add', 'Inserir componente')"
25638
+ [saveLabel]="tx('toolbar.save', 'Salvar página')"
25639
+ [undoLabel]="tx('toolbar.undo', 'Desfazer')"
25640
+ [redoLabel]="tx('toolbar.redo', 'Refazer')"
25641
+ [settingsLabel]="tx('toolbar.settings', 'Configurações da página')"
25518
25642
  [previewLabel]="tx('toolbar.preview', 'Pré-visualizar')"
25519
25643
  [exitPreviewLabel]="tx('toolbar.exitPreview', 'Voltar para edição')"
25520
25644
  (add)="onAddComponent()"