@praxisui/core 3.0.0-beta.5 → 3.0.0-beta.7

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.
@@ -1,35 +1,39 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Component, InjectionToken, Injectable, inject, Inject, makeEnvironmentProviders, Optional, ENVIRONMENT_INITIALIZER, ErrorHandler, APP_INITIALIZER, Input, ChangeDetectionStrategy, Directive, SecurityContext, EventEmitter, Output, ViewContainerRef, ContentChild, signal, HostListener, computed, ViewChild } from '@angular/core';
2
+ import { Component, InjectionToken, Injectable, inject, Inject, makeEnvironmentProviders, Optional, ENVIRONMENT_INITIALIZER, ErrorHandler, APP_INITIALIZER, EventEmitter, Output, Input, ChangeDetectionStrategy, Directive, SecurityContext, ViewContainerRef, ContentChild, signal, HostListener, computed, ViewChild } from '@angular/core';
3
3
  import * as i1 from '@angular/common/http';
4
- import { HttpHeaders, HttpClient, HttpParams, HttpContextToken, HTTP_INTERCEPTORS, withInterceptors } from '@angular/common/http';
4
+ import { HttpHeaders, HttpClient, HttpParams, HttpResponse, HttpContextToken, HTTP_INTERCEPTORS, withInterceptors } from '@angular/common/http';
5
5
  import { of, defer, throwError, from, EMPTY, BehaviorSubject, firstValueFrom, Subject } from 'rxjs';
6
6
  import { switchMap, take, map, catchError, concatMap, tap, shareReplay, takeUntil, toArray, finalize } from 'rxjs/operators';
7
- import * as i1$1 from '@angular/common';
7
+ import * as i1$2 from '@angular/common';
8
8
  import { Location, CommonModule } from '@angular/common';
9
9
  import { Router, ActivatedRoute } from '@angular/router';
10
- import * as i2$1 from '@angular/forms';
10
+ import * as i1$1 from '@angular/forms';
11
11
  import { Validators, FormGroup, FormControl, FormsModule } from '@angular/forms';
12
12
  import { MatSnackBar } from '@angular/material/snack-bar';
13
+ import * as i2 from '@angular/material/button';
14
+ import { MatButtonModule } from '@angular/material/button';
15
+ import * as i3 from '@angular/material/form-field';
16
+ import { MatFormFieldModule } from '@angular/material/form-field';
13
17
  import * as i4 from '@angular/material/icon';
14
18
  import { MatIconModule } from '@angular/material/icon';
19
+ import * as i5 from '@angular/material/input';
20
+ import { MatInputModule } from '@angular/material/input';
21
+ import * as i6 from '@angular/material/select';
22
+ import { MatSelectModule } from '@angular/material/select';
23
+ import * as i7 from '@angular/material/slide-toggle';
24
+ import { MatSlideToggleModule } from '@angular/material/slide-toggle';
25
+ import * as i5$1 from '@angular/material/tooltip';
26
+ import { MatTooltipModule } from '@angular/material/tooltip';
15
27
  import { DomSanitizer } from '@angular/platform-browser';
16
- import * as i2 from '@angular/material/button';
17
- import { MatButtonModule } from '@angular/material/button';
18
28
  import * as i4$1 from '@angular/material/menu';
19
29
  import { MatMenuModule } from '@angular/material/menu';
20
- import * as i5 from '@angular/material/tooltip';
21
- import { MatTooltipModule } from '@angular/material/tooltip';
22
- import * as i3 from '@angular/material/card';
30
+ import * as i3$1 from '@angular/material/card';
23
31
  import { MatCardModule } from '@angular/material/card';
24
- import * as i3$1 from '@angular/material/form-field';
25
- import { MatFormFieldModule } from '@angular/material/form-field';
26
- import * as i4$2 from '@angular/material/input';
27
- import { MatInputModule } from '@angular/material/input';
28
32
  import * as i8 from '@angular/material/chips';
29
33
  import { MatChipsModule } from '@angular/material/chips';
30
- import * as i1$2 from '@angular/material/dialog';
34
+ import * as i1$3 from '@angular/material/dialog';
31
35
  import { MAT_DIALOG_DATA, MatDialogModule, MatDialog } from '@angular/material/dialog';
32
- import * as i2$2 from '@angular/material/tabs';
36
+ import * as i2$1 from '@angular/material/tabs';
33
37
  import { MatTabsModule } from '@angular/material/tabs';
34
38
 
35
39
  class PraxisCore {
@@ -594,6 +598,24 @@ function resolveControlTypeAlias(value, fallback = FieldControlType.INPUT) {
594
598
  return raw;
595
599
  }
596
600
 
601
+ const VALUE_PRESENTATION_TYPES = new Set([
602
+ 'string',
603
+ 'number',
604
+ 'currency',
605
+ 'percentage',
606
+ 'date',
607
+ 'datetime',
608
+ 'time',
609
+ 'boolean',
610
+ ]);
611
+ const VALUE_PRESENTATION_STYLES = new Set([
612
+ 'default',
613
+ 'short',
614
+ 'medium',
615
+ 'long',
616
+ 'full',
617
+ 'compact',
618
+ ]);
597
619
  /**
598
620
  * SchemaNormalizerService
599
621
  * -----------------------
@@ -712,6 +734,41 @@ class SchemaNormalizerService {
712
734
  }
713
735
  return optionSource;
714
736
  }
737
+ parseValuePresentation(value) {
738
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
739
+ return undefined;
740
+ }
741
+ const typeToken = String(value.type ?? '').trim();
742
+ if (!typeToken || !VALUE_PRESENTATION_TYPES.has(typeToken)) {
743
+ if (typeToken) {
744
+ console.warn(`[SchemaNormalizer] Ignoring invalid x-ui.valuePresentation.type: ${typeToken}`);
745
+ }
746
+ return undefined;
747
+ }
748
+ const presentation = {
749
+ type: typeToken,
750
+ };
751
+ const styleToken = String(value.style ?? '').trim();
752
+ if (styleToken) {
753
+ if (VALUE_PRESENTATION_STYLES.has(styleToken)) {
754
+ presentation.style = styleToken;
755
+ }
756
+ else {
757
+ console.warn(`[SchemaNormalizer] Ignoring invalid x-ui.valuePresentation.style: ${styleToken}`);
758
+ }
759
+ }
760
+ const formatToken = String(value.format ?? '').trim();
761
+ if (formatToken) {
762
+ presentation.format = formatToken;
763
+ }
764
+ if (value.currency && typeof value.currency === 'object') {
765
+ presentation.currency = { ...value.currency };
766
+ }
767
+ if (value.number && typeof value.number === 'object') {
768
+ presentation.number = { ...value.number };
769
+ }
770
+ return presentation;
771
+ }
715
772
  /**
716
773
  * Converte string/Function em função real.
717
774
  * Atenção: usa `new Function` para avaliar strings – requer backend confiável.
@@ -955,6 +1012,10 @@ class SchemaNormalizerService {
955
1012
  field.numericFormat = numericFormat;
956
1013
  field.format = numericFormat;
957
1014
  }
1015
+ const valuePresentation = this.parseValuePresentation(ui.valuePresentation);
1016
+ if (valuePresentation) {
1017
+ field.valuePresentation = valuePresentation;
1018
+ }
958
1019
  if (ui.controlType) {
959
1020
  field.controlType = ui.controlType;
960
1021
  }
@@ -1458,6 +1519,8 @@ function resolveDefaultHeaders() {
1458
1519
  * HTTP-based storage that talks to praxis-config-starter user-config API.
1459
1520
  */
1460
1521
  class ApiConfigStorage {
1522
+ static unavailableLoadBaseUrls = new Set();
1523
+ static loadAvailabilityProbes = new Map();
1461
1524
  http = inject(HttpClient);
1462
1525
  opts = inject(API_CONFIG_STORAGE_OPTIONS, { optional: true });
1463
1526
  baseUrl = (this.opts?.baseUrl || '/api/praxis/config/ui').replace(/\/$/, '');
@@ -1472,13 +1535,86 @@ class ApiConfigStorage {
1472
1535
  // Simple in-memory cache of etag/payload per key to support 304 reuse
1473
1536
  cache = new Map();
1474
1537
  constructor() { }
1538
+ shouldLogLoadError(key, err) {
1539
+ if (err?.status === 404)
1540
+ return false;
1541
+ return this.shouldPropagateLoadError(key, err);
1542
+ }
1543
+ shouldLogSaveError(key, err) {
1544
+ if (err?.status === 404)
1545
+ return false;
1546
+ return this.shouldPropagateSaveError(key);
1547
+ }
1548
+ shouldLogClearError(key, err) {
1549
+ if (err?.status === 404)
1550
+ return false;
1551
+ return this.shouldPropagateClearError(key);
1552
+ }
1475
1553
  loadConfig(key) {
1554
+ if (ApiConfigStorage.unavailableLoadBaseUrls.has(this.baseUrl)) {
1555
+ return of(null);
1556
+ }
1557
+ const existingProbe = ApiConfigStorage.loadAvailabilityProbes.get(this.baseUrl);
1558
+ if (existingProbe) {
1559
+ return defer(() => from(existingProbe).pipe(switchMap((available) => available ? this.executeLoadConfigRequest(key, false) : of(null))));
1560
+ }
1561
+ return this.executeLoadConfigRequest(key, true);
1562
+ }
1563
+ executeLoadConfigRequest(key, establishAvailability) {
1476
1564
  const cached = this.cache.get(key);
1477
1565
  const etag = cached?.etag;
1478
1566
  const { type, id } = this.resolveKey(key);
1479
1567
  const url = `${this.baseUrl}`;
1480
1568
  const params = this.buildParams(type, id);
1481
1569
  const headers = this.buildHeaders(etag ? { 'If-None-Match': this.formatEtag(etag) } : {});
1570
+ let releaseProbe = null;
1571
+ if (establishAvailability) {
1572
+ let resolveProbe;
1573
+ const probe = new Promise((resolve) => {
1574
+ resolveProbe = resolve;
1575
+ });
1576
+ ApiConfigStorage.loadAvailabilityProbes.set(this.baseUrl, probe);
1577
+ releaseProbe = () => {
1578
+ ApiConfigStorage.loadAvailabilityProbes.delete(this.baseUrl);
1579
+ };
1580
+ const previousRelease = releaseProbe;
1581
+ releaseProbe = () => {
1582
+ resolveProbe(true);
1583
+ previousRelease();
1584
+ };
1585
+ const markUnavailable = () => {
1586
+ ApiConfigStorage.unavailableLoadBaseUrls.add(this.baseUrl);
1587
+ resolveProbe(false);
1588
+ previousRelease();
1589
+ };
1590
+ return this.http
1591
+ .get(url, { observe: 'response', headers, params })
1592
+ .pipe(map((resp) => {
1593
+ const nextEtag = this.stripQuotes(resp.headers.get('ETag'));
1594
+ const body = resp.body;
1595
+ if (nextEtag) {
1596
+ this.cache.set(key, { etag: nextEtag, payload: body?.payload });
1597
+ }
1598
+ releaseProbe?.();
1599
+ return body?.payload ?? null;
1600
+ }), catchError((err) => {
1601
+ if (err.status === 304 && cached?.payload !== undefined) {
1602
+ releaseProbe?.();
1603
+ return of(cached.payload);
1604
+ }
1605
+ if (err.status === 404) {
1606
+ markUnavailable();
1607
+ return of(null);
1608
+ }
1609
+ releaseProbe?.();
1610
+ if (this.shouldLogLoadError(key, err)) {
1611
+ console.warn('[ApiConfigStorage] load error', err);
1612
+ }
1613
+ return this.shouldPropagateLoadError(key, err)
1614
+ ? throwError(() => err)
1615
+ : of(null);
1616
+ }));
1617
+ }
1482
1618
  return this.http
1483
1619
  .get(url, { observe: 'response', headers, params })
1484
1620
  .pipe(map((resp) => {
@@ -1493,9 +1629,13 @@ class ApiConfigStorage {
1493
1629
  if (err.status === 304 && cached?.payload !== undefined) {
1494
1630
  return of(cached.payload);
1495
1631
  }
1496
- if (err.status === 404)
1632
+ if (err.status === 404) {
1633
+ ApiConfigStorage.unavailableLoadBaseUrls.add(this.baseUrl);
1497
1634
  return of(null);
1498
- console.warn('[ApiConfigStorage] load error', err);
1635
+ }
1636
+ if (this.shouldLogLoadError(key, err)) {
1637
+ console.warn('[ApiConfigStorage] load error', err);
1638
+ }
1499
1639
  return this.shouldPropagateLoadError(key, err)
1500
1640
  ? throwError(() => err)
1501
1641
  : of(null);
@@ -1513,7 +1653,9 @@ class ApiConfigStorage {
1513
1653
  const payload = resp.body?.payload ?? config;
1514
1654
  this.cache.set(key, { etag: nextEtag, payload });
1515
1655
  }), catchError((err) => {
1516
- console.warn('[ApiConfigStorage] save error', err);
1656
+ if (this.shouldLogSaveError(key, err)) {
1657
+ console.warn('[ApiConfigStorage] save error', err);
1658
+ }
1517
1659
  return this.shouldPropagateSaveError(key)
1518
1660
  ? throwError(() => err)
1519
1661
  // Soft-fail path: complete without next so runtime can treat as
@@ -1550,7 +1692,9 @@ class ApiConfigStorage {
1550
1692
  .pipe(map(() => {
1551
1693
  this.cache.delete(key);
1552
1694
  }), catchError((err) => {
1553
- console.warn('[ApiConfigStorage] clear error', err);
1695
+ if (this.shouldLogClearError(key, err)) {
1696
+ console.warn('[ApiConfigStorage] clear error', err);
1697
+ }
1554
1698
  return this.shouldPropagateClearError(key)
1555
1699
  ? throwError(() => err)
1556
1700
  // Soft-fail path: complete without next so runtime can treat as
@@ -1978,6 +2122,7 @@ class GenericCrudService {
1978
2122
  emptyEntity: 'A entidade não pode ser nula ou vazia.',
1979
2123
  unconfiguredService: 'Serviço não configurado. Chame configure() antes de usar.',
1980
2124
  };
2125
+ static unavailableFilteredSchemaUrls = new Set();
1981
2126
  baseApiUrl; // Root URL for the API
1982
2127
  apiUrl; // Full base path for the configured resource
1983
2128
  endpoints = {}; // Stores user-defined custom endpoints
@@ -2183,6 +2328,10 @@ class GenericCrudService {
2183
2328
  // Build cache key including the API origin (not the app origin)
2184
2329
  const schemaId = buildSchemaId({ path, operation, schemaType, includeInternalSchemas: false, tenant, locale, apiOrigin });
2185
2330
  const headersBase = composeHeadersWithVersion(entry);
2331
+ const shouldUseDirectSchemaEndpoint = GenericCrudService.unavailableFilteredSchemaUrls.has(filteredUrl);
2332
+ if (shouldUseDirectSchemaEndpoint) {
2333
+ return this.fetchDirectSchema(url, schemaId, apiOrigin, entry, locale, tenant, options?.httpContext);
2334
+ }
2186
2335
  return from(this.ensureSchemaCacheReady()).pipe(concatMap(() => from(this._schemaCache.get(schemaId)).pipe(concatMap((cached) => {
2187
2336
  let headers = (headersBase instanceof HttpHeaders ? headersBase : new HttpHeaders(headersBase));
2188
2337
  if (cached?.schemaHash) {
@@ -2279,6 +2428,10 @@ class GenericCrudService {
2279
2428
  }),
2280
2429
  // Angular HttpClient treats 304 as error; also handle status 0 (CORS/network) by reusing cache
2281
2430
  catchError((err) => {
2431
+ if (err?.status === 404) {
2432
+ GenericCrudService.unavailableFilteredSchemaUrls.add(filteredUrl);
2433
+ return this.fetchDirectSchema(url, schemaId, apiOrigin, entry, locale, tenant, options?.httpContext);
2434
+ }
2282
2435
  if (err?.status === 304 || err?.status === 0) {
2283
2436
  return from(this._schemaCache.get(schemaId)).pipe(concatMap((cached) => {
2284
2437
  if (cached?.schema) {
@@ -2335,6 +2488,69 @@ class GenericCrudService {
2335
2488
  return this.handleError(err);
2336
2489
  }))), shareReplay(1));
2337
2490
  }
2491
+ fetchDirectSchema(url, schemaId, apiOrigin, entry, locale, tenant, httpContext) {
2492
+ return from(this.ensureSchemaCacheReady()).pipe(concatMap(() => from(this._schemaCache.get(schemaId)).pipe(concatMap((cached) => {
2493
+ const baseHeaders = composeHeadersWithVersion(entry);
2494
+ let headers = (baseHeaders instanceof HttpHeaders ? baseHeaders : new HttpHeaders(baseHeaders));
2495
+ if (cached?.schemaHash) {
2496
+ headers = headers.set('If-None-Match', `"${cached.schemaHash}"`);
2497
+ }
2498
+ if (locale)
2499
+ headers = headers.set('Accept-Language', locale);
2500
+ if (tenant)
2501
+ headers = headers.set('X-Tenant', tenant);
2502
+ return this.http.get(url, {
2503
+ headers,
2504
+ observe: 'response',
2505
+ context: httpContext,
2506
+ }).pipe(concatMap((resp) => {
2507
+ const response = resp instanceof HttpResponse
2508
+ ? resp
2509
+ : new HttpResponse({ status: 200, body: resp });
2510
+ const body = response.body;
2511
+ const etag = response.headers.get('ETag') || '';
2512
+ const xHash = response.headers.get('X-Schema-Hash');
2513
+ const schemaHash = (xHash || etag).replace(/^W\//, '').replace(/^"|"$/g, '');
2514
+ const now = new Date().toISOString();
2515
+ const entryToCache = {
2516
+ schema: body,
2517
+ schemaHash,
2518
+ meta: {
2519
+ version: '2.0.0',
2520
+ name: 'Grid/Response Schema',
2521
+ createdAt: now,
2522
+ updatedAt: now,
2523
+ resourcePath: this.resourcePath,
2524
+ schemaId,
2525
+ apiOrigin,
2526
+ },
2527
+ };
2528
+ this._schemaCache.set(schemaId, entryToCache);
2529
+ this._lastSchemaInfo = { schemaId, schemaHash };
2530
+ try {
2531
+ const idField = body?.['x-ui']?.resource?.idField;
2532
+ this._lastResourceMeta = { idField, resourcePath: this.resourcePath };
2533
+ }
2534
+ catch { }
2535
+ return of(this.schemaNormalizer.normalizeSchema(body));
2536
+ }), catchError((err) => {
2537
+ if ((err?.status === 304 || err?.status === 0) && cached?.schema) {
2538
+ try {
2539
+ const idField = cached.schema?.['x-ui']?.resource?.idField;
2540
+ this._lastResourceMeta = {
2541
+ idField: idField || this._lastResourceMeta.idField,
2542
+ resourcePath: this.resourcePath,
2543
+ };
2544
+ }
2545
+ catch { }
2546
+ const cachedHash = cached.schemaHash || '';
2547
+ this._lastSchemaInfo = { schemaId, schemaHash: cachedHash };
2548
+ return of(this.schemaNormalizer.normalizeSchema(cached.schema));
2549
+ }
2550
+ return this.handleError(err);
2551
+ }));
2552
+ }))));
2553
+ }
2338
2554
  /** Retorna o campo identificador do recurso (ex.: 'id', 'codigo'), quando derivado do schema. */
2339
2555
  getResourceIdField() {
2340
2556
  const key = (this._lastResourceMeta?.idField || '').trim();
@@ -4418,6 +4634,147 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
4418
4634
  args: [{ providedIn: 'root' }]
4419
4635
  }] });
4420
4636
 
4637
+ class SurfaceBindingRuntimeService {
4638
+ resolveWidget(widget, bindings, actionPayload, actionContext, explicitContext) {
4639
+ const context = this.buildContext(actionPayload, actionContext, explicitContext);
4640
+ let resolvedWidget = this.clone(widget);
4641
+ resolvedWidget = this.resolveTemplate(resolvedWidget, context);
4642
+ for (const binding of bindings || []) {
4643
+ const targetPath = this.normalizeTargetPath(binding?.to);
4644
+ if (!targetPath)
4645
+ continue;
4646
+ const value = this.resolveBindingValue(binding, context);
4647
+ resolvedWidget = this.setValueAtPath(resolvedWidget, targetPath, value);
4648
+ }
4649
+ return resolvedWidget;
4650
+ }
4651
+ extractByPath(obj, path) {
4652
+ if (!path)
4653
+ return obj;
4654
+ return String(path)
4655
+ .split('.')
4656
+ .reduce((acc, key) => acc?.[key], obj);
4657
+ }
4658
+ resolveTemplate(node, context) {
4659
+ if (node == null)
4660
+ return node;
4661
+ if (Array.isArray(node))
4662
+ return node.map((value) => this.resolveTemplate(value, context));
4663
+ if (typeof node === 'object') {
4664
+ const out = {};
4665
+ for (const [key, value] of Object.entries(node)) {
4666
+ out[key] = this.resolveTemplate(value, context);
4667
+ }
4668
+ return out;
4669
+ }
4670
+ if (typeof node === 'string') {
4671
+ const exact = node.match(/^\s*\$\{([^}]+)\}\s*$/);
4672
+ if (exact) {
4673
+ return this.extractByPath(context, exact[1].trim());
4674
+ }
4675
+ return node.replace(/\$\{([^}]+)\}/g, (_match, path) => {
4676
+ const value = this.extractByPath(context, String(path).trim());
4677
+ return value != null ? String(value) : '';
4678
+ });
4679
+ }
4680
+ return node;
4681
+ }
4682
+ setValueAtPath(obj, rawPath, value) {
4683
+ const path = this.tokenizePath(rawPath);
4684
+ if (!path.length)
4685
+ return obj;
4686
+ const clone = Array.isArray(obj) ? obj.slice() : { ...obj };
4687
+ let cursor = clone;
4688
+ for (let i = 0; i < path.length; i++) {
4689
+ const key = path[i];
4690
+ const isLast = i === path.length - 1;
4691
+ if (isLast) {
4692
+ cursor[key] = value;
4693
+ continue;
4694
+ }
4695
+ const next = cursor[key];
4696
+ const nextIsIndex = typeof path[i + 1] === 'number';
4697
+ const created = next != null
4698
+ ? (Array.isArray(next) ? next.slice() : { ...next })
4699
+ : (nextIsIndex ? [] : {});
4700
+ cursor[key] = created;
4701
+ cursor = created;
4702
+ }
4703
+ return clone;
4704
+ }
4705
+ buildContext(actionPayload, actionContext, explicitContext) {
4706
+ return {
4707
+ payload: actionContext?.payload,
4708
+ context: explicitContext || {},
4709
+ pageContext: actionContext?.pageContext || undefined,
4710
+ meta: actionContext?.meta || undefined,
4711
+ runtime: actionContext?.runtime || undefined,
4712
+ action: actionPayload,
4713
+ sourceId: actionContext?.sourceId,
4714
+ widgetKey: actionContext?.widgetKey,
4715
+ output: actionContext?.output,
4716
+ };
4717
+ }
4718
+ resolveBindingValue(binding, context) {
4719
+ const mode = binding.mode || (binding.value !== undefined ? 'constant' : 'path');
4720
+ if (mode === 'constant')
4721
+ return this.clone(binding.value);
4722
+ if (mode === 'template') {
4723
+ const templateSource = binding.value !== undefined ? binding.value : binding.from;
4724
+ return this.resolveTemplate(templateSource, context);
4725
+ }
4726
+ return this.extractByPath(context, binding.from);
4727
+ }
4728
+ normalizeTargetPath(rawPath) {
4729
+ const path = String(rawPath || '').trim();
4730
+ if (!path)
4731
+ return '';
4732
+ return path.startsWith('widget.') ? path.slice('widget.'.length) : path;
4733
+ }
4734
+ tokenizePath(path) {
4735
+ const tokens = [];
4736
+ let buffer = '';
4737
+ for (let i = 0; i < path.length; i++) {
4738
+ const char = path[i];
4739
+ if (char === '.') {
4740
+ if (buffer) {
4741
+ tokens.push(buffer);
4742
+ buffer = '';
4743
+ }
4744
+ continue;
4745
+ }
4746
+ if (char === '[') {
4747
+ if (buffer) {
4748
+ tokens.push(buffer);
4749
+ buffer = '';
4750
+ }
4751
+ let num = '';
4752
+ i++;
4753
+ while (i < path.length && path[i] !== ']') {
4754
+ num += path[i];
4755
+ i++;
4756
+ }
4757
+ const index = Number(num);
4758
+ tokens.push(Number.isNaN(index) ? num : index);
4759
+ continue;
4760
+ }
4761
+ buffer += char;
4762
+ }
4763
+ if (buffer)
4764
+ tokens.push(buffer);
4765
+ return tokens;
4766
+ }
4767
+ clone(value) {
4768
+ return value == null ? value : JSON.parse(JSON.stringify(value));
4769
+ }
4770
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SurfaceBindingRuntimeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4771
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SurfaceBindingRuntimeService, providedIn: 'root' });
4772
+ }
4773
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SurfaceBindingRuntimeService, decorators: [{
4774
+ type: Injectable,
4775
+ args: [{ providedIn: 'root' }]
4776
+ }] });
4777
+
4421
4778
  const GLOBAL_ACTION_HANDLERS = new InjectionToken('GLOBAL_ACTION_HANDLERS');
4422
4779
  const GLOBAL_DIALOG_SERVICE = new InjectionToken('GLOBAL_DIALOG_SERVICE');
4423
4780
  const GLOBAL_TOAST_SERVICE = new InjectionToken('GLOBAL_TOAST_SERVICE');
@@ -4432,6 +4789,8 @@ function provideGlobalActionHandler(entry) {
4432
4789
  };
4433
4790
  }
4434
4791
 
4792
+ const GLOBAL_SURFACE_SERVICE = new InjectionToken('GLOBAL_SURFACE_SERVICE');
4793
+
4435
4794
  class GlobalActionService {
4436
4795
  handlers = new Map();
4437
4796
  router = (() => { try {
@@ -4459,10 +4818,12 @@ class GlobalActionService {
4459
4818
  return null;
4460
4819
  } })();
4461
4820
  dialog = inject(GLOBAL_DIALOG_SERVICE, { optional: true });
4821
+ surface = inject(GLOBAL_SURFACE_SERVICE, { optional: true });
4462
4822
  toast = inject(GLOBAL_TOAST_SERVICE, { optional: true });
4463
4823
  analytics = inject(GLOBAL_ANALYTICS_SERVICE, { optional: true });
4464
4824
  api = inject(GLOBAL_API_CLIENT, { optional: true });
4465
4825
  guardResolver = inject(GLOBAL_ROUTE_GUARD_RESOLVER, { optional: true });
4826
+ surfaceBindingRuntime = inject(SurfaceBindingRuntimeService);
4466
4827
  constructor() {
4467
4828
  const entries = inject(GLOBAL_ACTION_HANDLERS, { optional: true });
4468
4829
  (entries || []).forEach((e) => this.register(e.id, e.handler));
@@ -4535,6 +4896,17 @@ class GlobalActionService {
4535
4896
  const data = await this.dialog.open(payload || {});
4536
4897
  return { success: true, data };
4537
4898
  });
4899
+ this.register('surface.open', async (payload, context) => {
4900
+ if (!this.surface)
4901
+ return { success: false, error: 'Surface service not available' };
4902
+ const surfacePayload = payload;
4903
+ if (!surfacePayload?.widget?.id) {
4904
+ return { success: false, error: 'surface.open requires widget.id' };
4905
+ }
4906
+ const resolvedWidget = this.surfaceBindingRuntime.resolveWidget(surfacePayload.widget, surfacePayload.bindings, surfacePayload, context, surfacePayload.context);
4907
+ const data = await this.surface.open({ ...surfacePayload, widget: resolvedWidget }, context);
4908
+ return { success: true, data };
4909
+ });
4538
4910
  this.register('toast.success', async (payload) => {
4539
4911
  const message = payload?.message || payload;
4540
4912
  if (!message)
@@ -7868,6 +8240,42 @@ const PRAXIS_GLOBAL_ACTION_CATALOG = [
7868
8240
  example: { componentId: 'praxis-dynamic-form', inputs: { formId: 'clientes' } },
7869
8241
  },
7870
8242
  },
8243
+ {
8244
+ id: 'surface.open',
8245
+ label: 'Abrir Surface',
8246
+ icon: 'open_in_new',
8247
+ description: 'Abre modal ou drawer com um widget registrado e bindings dinâmicos.',
8248
+ payloadSchema: {
8249
+ type: 'object',
8250
+ properties: {
8251
+ presentation: { type: 'string', description: 'Tipo de apresentação: modal ou drawer.' },
8252
+ title: { type: 'string', description: 'Título opcional da surface.' },
8253
+ subtitle: { type: 'string', description: 'Subtítulo opcional da surface.' },
8254
+ icon: { type: 'string', description: 'Ícone opcional da surface.' },
8255
+ size: { type: 'object', description: 'Configuração opcional de tamanho da surface.' },
8256
+ widget: { type: 'object', description: 'WidgetDefinition do componente a ser renderizado.' },
8257
+ bindings: { type: 'array', description: 'Bindings declarativos aplicados sobre widget.inputs.*.' },
8258
+ context: { type: 'object', description: 'Contexto explícito adicional exposto ao runtime de bindings.' },
8259
+ },
8260
+ required: ['presentation', 'widget'],
8261
+ example: {
8262
+ presentation: 'drawer',
8263
+ title: 'Detalhes do Funcionário',
8264
+ widget: {
8265
+ id: 'praxis-dynamic-form',
8266
+ bindingOrder: ['resourcePath', 'formId', 'resourceId'],
8267
+ inputs: {
8268
+ resourcePath: 'human-resources/employees',
8269
+ formId: 'employee-detail',
8270
+ mode: 'view',
8271
+ },
8272
+ },
8273
+ bindings: [
8274
+ { from: 'payload.row.id', to: 'widget.inputs.resourceId' },
8275
+ ],
8276
+ },
8277
+ },
8278
+ },
7871
8279
  {
7872
8280
  id: 'toast.success',
7873
8281
  label: 'Toast Sucesso',
@@ -8007,6 +8415,17 @@ const GLOBAL_ACTION_CATALOG = [
8007
8415
  example: 'https://docs.exemplo.com',
8008
8416
  },
8009
8417
  },
8418
+ {
8419
+ id: 'surface.open',
8420
+ label: 'Abrir Surface',
8421
+ description: 'Abre modal ou drawer com um componente registrado e payload dinâmico.',
8422
+ param: {
8423
+ label: 'Payload (JSON)',
8424
+ placeholder: '{"presentation":"drawer","widget":{"id":"praxis-dynamic-form","inputs":{"resourcePath":"employees","formId":"employee-detail"}}}',
8425
+ hint: 'Use o editor especializado para configurar widget, inputs e bindings da surface.',
8426
+ example: '{"presentation":"drawer","widget":{"id":"praxis-dynamic-form","inputs":{"resourcePath":"employees","formId":"employee-detail"}}}',
8427
+ },
8428
+ },
8010
8429
  {
8011
8430
  id: 'showAlert',
8012
8431
  label: 'Alerta simples',
@@ -8178,6 +8597,110 @@ function findGlobalActionSpec(id) {
8178
8597
  return GLOBAL_ACTION_CATALOG.find((item) => item.id === id);
8179
8598
  }
8180
8599
 
8600
+ const SURFACE_OPEN_I18N_NAMESPACE = 'surfaceOpen';
8601
+ const SURFACE_OPEN_I18N_CONFIG = {
8602
+ namespaces: {
8603
+ [SURFACE_OPEN_I18N_NAMESPACE]: {
8604
+ 'pt-BR': {
8605
+ 'editor.presets.title': 'Presets',
8606
+ 'editor.presets.help': 'Aplique um preset para começar com inputs mínimos e depois configure bindings conforme a origem da action.',
8607
+ 'editor.surface.title': 'Surface',
8608
+ 'editor.surface.presentation': 'Apresentação',
8609
+ 'editor.surface.presentation.modal': 'Modal',
8610
+ 'editor.surface.presentation.drawer': 'Drawer',
8611
+ 'editor.surface.titleLabel': 'Título',
8612
+ 'editor.surface.subtitleLabel': 'Subtítulo',
8613
+ 'editor.surface.iconLabel': 'Ícone',
8614
+ 'editor.surface.widthLabel': 'Largura',
8615
+ 'editor.surface.heightLabel': 'Altura',
8616
+ 'editor.surface.minWidthLabel': 'Largura mínima',
8617
+ 'editor.surface.maxWidthLabel': 'Largura máxima',
8618
+ 'editor.component.title': 'Componente',
8619
+ 'editor.component.select': 'Componente registrado',
8620
+ 'editor.component.selectPlaceholder': '-- Selecionar --',
8621
+ 'editor.component.bindingOrder': 'bindingOrder (CSV)',
8622
+ 'editor.component.bindingOrderPlaceholder': 'resourcePath, formId, resourceId',
8623
+ 'editor.inputs.title': 'Inputs',
8624
+ 'editor.inputs.empty': 'Selecione um componente registrado para configurar inputs públicos.',
8625
+ 'editor.bindings.title': 'Bindings',
8626
+ 'editor.bindings.add': 'Binding',
8627
+ 'editor.bindings.source': 'Origem',
8628
+ 'editor.bindings.target': 'Destino',
8629
+ 'editor.bindings.mode': 'Modo',
8630
+ 'editor.bindings.constantValue': 'Valor constante',
8631
+ 'editor.bindings.remove': 'Remover binding',
8632
+ 'editor.bindings.empty': 'Nenhum binding configurado ainda.',
8633
+ 'editor.bindings.suggestedSources': 'Origens sugeridas: {{value}}',
8634
+ 'editor.bindings.suggestedTargets': 'Destinos sugeridos: {{value}}',
8635
+ 'editor.context.title': 'Contexto extra',
8636
+ 'editor.context.label': 'context (JSON)',
8637
+ 'editor.context.hint': 'Opcional. Mesclado ao contexto da action no runtime.',
8638
+ 'editor.validation.invalidJson': 'JSON inválido.',
8639
+ 'editor.validation.objectJson': 'Informe um objeto JSON.',
8640
+ 'preset.praxis-dynamic-form.label': 'Formulário',
8641
+ 'preset.praxis-dynamic-form.description': 'Abre um formulário dinâmico com resourcePath, formId e resourceId.',
8642
+ 'preset.praxis-table.label': 'Tabela',
8643
+ 'preset.praxis-table.description': 'Abre uma tabela configurável com resourcePath, tableId e queryContext.',
8644
+ 'preset.praxis-list.label': 'Lista',
8645
+ 'preset.praxis-list.description': 'Abre uma lista com listId e config básicos.',
8646
+ 'preset.praxis-crud.label': 'CRUD',
8647
+ 'preset.praxis-crud.description': 'Abre a surface de CRUD com resourcePath, formId e bindings configuráveis.',
8648
+ 'action.surfaceOpen.label': 'Abrir Surface',
8649
+ 'action.surfaceOpen.description': 'Abre modal ou drawer com um componente registrado e payload dinâmico.',
8650
+ 'action.surfaceOpen.hint': 'Use o editor especializado para configurar widget, inputs e bindings da surface.',
8651
+ },
8652
+ 'en-US': {
8653
+ 'editor.presets.title': 'Presets',
8654
+ 'editor.presets.help': 'Apply a preset to start with minimal inputs, then configure bindings for the action source.',
8655
+ 'editor.surface.title': 'Surface',
8656
+ 'editor.surface.presentation': 'Presentation',
8657
+ 'editor.surface.presentation.modal': 'Modal',
8658
+ 'editor.surface.presentation.drawer': 'Drawer',
8659
+ 'editor.surface.titleLabel': 'Title',
8660
+ 'editor.surface.subtitleLabel': 'Subtitle',
8661
+ 'editor.surface.iconLabel': 'Icon',
8662
+ 'editor.surface.widthLabel': 'Width',
8663
+ 'editor.surface.heightLabel': 'Height',
8664
+ 'editor.surface.minWidthLabel': 'Min width',
8665
+ 'editor.surface.maxWidthLabel': 'Max width',
8666
+ 'editor.component.title': 'Component',
8667
+ 'editor.component.select': 'Registered component',
8668
+ 'editor.component.selectPlaceholder': '-- Select --',
8669
+ 'editor.component.bindingOrder': 'bindingOrder (CSV)',
8670
+ 'editor.component.bindingOrderPlaceholder': 'resourcePath, formId, resourceId',
8671
+ 'editor.inputs.title': 'Inputs',
8672
+ 'editor.inputs.empty': 'Select a registered component to configure public inputs.',
8673
+ 'editor.bindings.title': 'Bindings',
8674
+ 'editor.bindings.add': 'Binding',
8675
+ 'editor.bindings.source': 'Source',
8676
+ 'editor.bindings.target': 'Target',
8677
+ 'editor.bindings.mode': 'Mode',
8678
+ 'editor.bindings.constantValue': 'Constant value',
8679
+ 'editor.bindings.remove': 'Remove binding',
8680
+ 'editor.bindings.empty': 'No bindings configured yet.',
8681
+ 'editor.bindings.suggestedSources': 'Suggested sources: {{value}}',
8682
+ 'editor.bindings.suggestedTargets': 'Suggested targets: {{value}}',
8683
+ 'editor.context.title': 'Extra context',
8684
+ 'editor.context.label': 'context (JSON)',
8685
+ 'editor.context.hint': 'Optional. Merged into the action context at runtime.',
8686
+ 'editor.validation.invalidJson': 'Invalid JSON.',
8687
+ 'editor.validation.objectJson': 'Provide a JSON object.',
8688
+ 'preset.praxis-dynamic-form.label': 'Form',
8689
+ 'preset.praxis-dynamic-form.description': 'Opens a dynamic form with resourcePath, formId and resourceId.',
8690
+ 'preset.praxis-table.label': 'Table',
8691
+ 'preset.praxis-table.description': 'Opens a configurable table with resourcePath, tableId and queryContext.',
8692
+ 'preset.praxis-list.label': 'List',
8693
+ 'preset.praxis-list.description': 'Opens a list with basic listId and config inputs.',
8694
+ 'preset.praxis-crud.label': 'CRUD',
8695
+ 'preset.praxis-crud.description': 'Opens the CRUD surface with resourcePath, formId and configurable bindings.',
8696
+ 'action.surfaceOpen.label': 'Open Surface',
8697
+ 'action.surfaceOpen.description': 'Opens a modal or drawer with a registered component and dynamic payload.',
8698
+ 'action.surfaceOpen.hint': 'Use the dedicated editor to configure the surface widget, inputs and bindings.',
8699
+ },
8700
+ },
8701
+ },
8702
+ };
8703
+
8181
8704
  const SETTINGS_PANEL_BRIDGE = new InjectionToken('SETTINGS_PANEL_BRIDGE');
8182
8705
  // Optional data token for consumers that still use DI-based data passing.
8183
8706
  // Prefer using component inputs when opening via SettingsPanelBridge.
@@ -8390,6 +8913,169 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
8390
8913
  args: [PRAXIS_I18N_TRANSLATOR]
8391
8914
  }] }] });
8392
8915
 
8916
+ const DEFAULT_LOCALE = 'en-US';
8917
+ const DEFAULT_DATE_TIME = {
8918
+ dateFormat: 'shortDate',
8919
+ timeFormat: 'shortTime',
8920
+ dateTimeFormat: 'short',
8921
+ firstDayOfWeek: 0,
8922
+ relativeTime: false,
8923
+ };
8924
+ const DEFAULT_NUMBER = {
8925
+ decimalSeparator: '.',
8926
+ thousandsSeparator: ',',
8927
+ defaultPrecision: 2,
8928
+ negativeSign: '-',
8929
+ negativeSignPosition: 'before',
8930
+ };
8931
+ const DEFAULT_CURRENCY = {
8932
+ code: 'USD',
8933
+ symbol: '$',
8934
+ position: 'before',
8935
+ spacing: false,
8936
+ precision: 2,
8937
+ };
8938
+ const IMPLICIT_FORMAT_TYPES = new Set([
8939
+ 'number',
8940
+ 'currency',
8941
+ 'percentage',
8942
+ 'date',
8943
+ 'datetime',
8944
+ 'time',
8945
+ ]);
8946
+ function resolveValuePresentation(config, context) {
8947
+ const localization = context?.localization ?? null;
8948
+ const locale = resolveValuePresentationLocale(context, localization);
8949
+ const number = resolveNumberLocaleConfig(config, localization);
8950
+ const currency = resolveCurrencyLocaleConfig(config, localization);
8951
+ const dateTime = resolveDateTimeLocaleConfig(localization);
8952
+ return {
8953
+ type: config.type,
8954
+ locale,
8955
+ style: config.style,
8956
+ format: config.format ||
8957
+ resolveDefaultValuePresentationFormat(config.type, config.style, {
8958
+ localization,
8959
+ number,
8960
+ currency,
8961
+ dateTime,
8962
+ }),
8963
+ currency: config.type === 'currency' ? currency : undefined,
8964
+ number: config.type === 'number' || config.type === 'percentage'
8965
+ ? number
8966
+ : undefined,
8967
+ dateTime: config.type === 'date' ||
8968
+ config.type === 'datetime' ||
8969
+ config.type === 'time'
8970
+ ? dateTime
8971
+ : undefined,
8972
+ };
8973
+ }
8974
+ function resolveValuePresentationLocale(context, localization) {
8975
+ return (context?.valueLocale ||
8976
+ localization?.locale ||
8977
+ context?.surfaceLocale ||
8978
+ context?.appLocale ||
8979
+ DEFAULT_LOCALE);
8980
+ }
8981
+ function supportsImplicitValuePresentation(type) {
8982
+ return !!type && IMPLICIT_FORMAT_TYPES.has(type);
8983
+ }
8984
+ function resolveDefaultValuePresentationFormat(type, style, context) {
8985
+ switch (type) {
8986
+ case 'date':
8987
+ return resolveDateFormat(style, context.dateTime.dateFormat);
8988
+ case 'datetime':
8989
+ return resolveDateTimeFormat(style, context.dateTime.dateTimeFormat);
8990
+ case 'time':
8991
+ return resolveTimeFormat(style, context.dateTime.timeFormat);
8992
+ case 'number':
8993
+ return resolveDigitsFormat(context.number.defaultPrecision);
8994
+ case 'currency':
8995
+ return `${context.currency.code}|symbol|${context.currency.precision}`;
8996
+ case 'percentage':
8997
+ return (context.localization?.formatting?.percentageFormat ||
8998
+ resolveDigitsFormat(context.number.defaultPrecision));
8999
+ default:
9000
+ return undefined;
9001
+ }
9002
+ }
9003
+ function resolveDateTimeLocaleConfig(localization) {
9004
+ return {
9005
+ ...DEFAULT_DATE_TIME,
9006
+ ...(localization?.dateTime || {}),
9007
+ };
9008
+ }
9009
+ function resolveNumberLocaleConfig(config, localization) {
9010
+ return {
9011
+ ...DEFAULT_NUMBER,
9012
+ ...(localization?.number || {}),
9013
+ ...(config.number || {}),
9014
+ };
9015
+ }
9016
+ function resolveCurrencyLocaleConfig(config, localization) {
9017
+ return {
9018
+ ...DEFAULT_CURRENCY,
9019
+ ...(localization?.currency || {}),
9020
+ ...(config.currency || {}),
9021
+ };
9022
+ }
9023
+ function resolveDateFormat(style, fallback) {
9024
+ switch (style) {
9025
+ case 'short':
9026
+ return 'shortDate';
9027
+ case 'medium':
9028
+ return 'mediumDate';
9029
+ case 'long':
9030
+ return 'longDate';
9031
+ case 'full':
9032
+ return 'fullDate';
9033
+ default:
9034
+ return fallback || DEFAULT_DATE_TIME.dateFormat;
9035
+ }
9036
+ }
9037
+ function resolveDateTimeFormat(style, fallback) {
9038
+ switch (style) {
9039
+ case 'short':
9040
+ return 'short';
9041
+ case 'medium':
9042
+ return 'medium';
9043
+ case 'long':
9044
+ return 'long';
9045
+ case 'full':
9046
+ return 'full';
9047
+ default:
9048
+ return fallback || DEFAULT_DATE_TIME.dateTimeFormat;
9049
+ }
9050
+ }
9051
+ function resolveTimeFormat(style, fallback) {
9052
+ switch (style) {
9053
+ case 'short':
9054
+ return 'shortTime';
9055
+ case 'medium':
9056
+ return 'mediumTime';
9057
+ case 'long':
9058
+ return 'longTime';
9059
+ case 'full':
9060
+ return 'fullTime';
9061
+ default:
9062
+ return fallback || DEFAULT_DATE_TIME.timeFormat;
9063
+ }
9064
+ }
9065
+ function resolveDigitsFormat(defaultPrecision) {
9066
+ const precision = clampPrecision(defaultPrecision);
9067
+ return `1.${precision}-${precision}`;
9068
+ }
9069
+ function clampPrecision(value) {
9070
+ if (typeof value !== 'number' || Number.isNaN(value)) {
9071
+ return DEFAULT_NUMBER.defaultPrecision;
9072
+ }
9073
+ if (value < 0) {
9074
+ return 0;
9075
+ }
9076
+ return Math.floor(value);
9077
+ }
9078
+
8393
9079
  /**
8394
9080
  * Enum que define os tipos de dados (`TYPE`) disponíveis para configuração dos campos de formulário.
8395
9081
  */
@@ -9790,6 +10476,7 @@ const RULE_PROPERTY_SCHEMA = {
9790
10476
  ],
9791
10477
  },
9792
10478
  { name: 'sectionHeader.initialsMaxLength', type: 'number', label: 'Máximo de iniciais' },
10479
+ { name: 'headerActions', type: 'object', label: 'Ações do cabeçalho' },
9793
10480
  { name: 'className', type: 'string', label: 'Classe CSS' },
9794
10481
  { name: 'style', type: 'object', label: 'Estilos inline' },
9795
10482
  { name: 'borderColor', type: 'string', label: 'Cor da borda' },
@@ -10894,6 +11581,66 @@ function createPersistedPage(identity, page, opts) {
10894
11581
 
10895
11582
  // Global Dialog domain types for GlobalConfig (kept independent of @praxisui/dialog types)
10896
11583
 
11584
+ function normalizeRecord(record) {
11585
+ if (!record || typeof record !== 'object')
11586
+ return null;
11587
+ const next = Object.entries(record).reduce((acc, [key, value]) => {
11588
+ if (value === null || value === undefined)
11589
+ return acc;
11590
+ if (Array.isArray(value) && value.length === 0)
11591
+ return acc;
11592
+ if (typeof value === 'string' && value.trim() === '')
11593
+ return acc;
11594
+ acc[key] = value;
11595
+ return acc;
11596
+ }, {});
11597
+ return Object.keys(next).length ? next : null;
11598
+ }
11599
+ function normalizePraxisDataQueryContext(context) {
11600
+ if (!context || typeof context !== 'object')
11601
+ return null;
11602
+ const filters = normalizeRecord(context.filters);
11603
+ const meta = normalizeRecord(context.meta);
11604
+ const sort = Array.isArray(context.sort)
11605
+ ? context.sort
11606
+ .map((item) => (typeof item === 'string' ? item.trim() : ''))
11607
+ .filter((item) => item.length > 0)
11608
+ : [];
11609
+ const limit = typeof context.limit === 'number' && Number.isFinite(context.limit)
11610
+ ? Math.max(0, Math.trunc(context.limit))
11611
+ : null;
11612
+ const page = context.page && typeof context.page === 'object'
11613
+ ? {
11614
+ index: typeof context.page.index === 'number' && Number.isFinite(context.page.index)
11615
+ ? Math.max(0, Math.trunc(context.page.index))
11616
+ : null,
11617
+ size: typeof context.page.size === 'number' && Number.isFinite(context.page.size)
11618
+ ? Math.max(0, Math.trunc(context.page.size))
11619
+ : null,
11620
+ }
11621
+ : null;
11622
+ const next = {};
11623
+ if (filters)
11624
+ next.filters = filters;
11625
+ if (sort.length)
11626
+ next.sort = sort;
11627
+ if (limit !== null)
11628
+ next.limit = limit;
11629
+ if (page && (page.index !== null || page.size !== null))
11630
+ next.page = page;
11631
+ if (meta)
11632
+ next.meta = meta;
11633
+ return Object.keys(next).length ? next : null;
11634
+ }
11635
+ function resolvePraxisFilterCriteria(filterCriteria, queryContext) {
11636
+ const normalizedFilterCriteria = normalizeRecord(filterCriteria);
11637
+ const normalizedQueryContext = normalizePraxisDataQueryContext(queryContext);
11638
+ return {
11639
+ ...(normalizedFilterCriteria || {}),
11640
+ ...(normalizedQueryContext?.filters || {}),
11641
+ };
11642
+ }
11643
+
10897
11644
  const DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP = {
10898
11645
  'pdx-text-input': FieldControlType.INPUT,
10899
11646
  'pdx-email-input': FieldControlType.EMAIL_INPUT,
@@ -11155,6 +11902,18 @@ function mapFieldDefinitionToMetadata(field) {
11155
11902
  if (field.type) {
11156
11903
  metadata.dataType = field.type;
11157
11904
  }
11905
+ if (field.valuePresentation) {
11906
+ const valuePresentation = {
11907
+ ...field.valuePresentation,
11908
+ };
11909
+ if (field.valuePresentation.currency) {
11910
+ valuePresentation.currency = { ...field.valuePresentation.currency };
11911
+ }
11912
+ if (field.valuePresentation.number) {
11913
+ valuePresentation.number = { ...field.valuePresentation.number };
11914
+ }
11915
+ metadata.valuePresentation = valuePresentation;
11916
+ }
11158
11917
  const simpleProps = [
11159
11918
  'order',
11160
11919
  'group',
@@ -11454,15 +12213,19 @@ function normalizeTargets(rule) {
11454
12213
  (Array.isArray(rule.targetFields) && rule.targetFields.length && rule.targetFields) ||
11455
12214
  [];
11456
12215
  const targetType = rule.targetType ||
11457
- (rawTargets.some((t) => t?.startsWith('section:')) ? 'section'
11458
- : rawTargets.some((t) => t?.startsWith('action:')) ? 'action'
11459
- : rawTargets.some((t) => t?.startsWith('row:')) ? 'row'
11460
- : rawTargets.some((t) => t?.startsWith('column:')) ? 'column'
11461
- : 'field');
12216
+ (rawTargets.some((t) => isScopedSectionHeaderActionTarget(t) || isUnscopedSectionHeaderActionTarget(t)) ? 'action'
12217
+ : rawTargets.some((t) => t?.startsWith('section:')) ? 'section'
12218
+ : rawTargets.some((t) => t?.startsWith('action:')) ? 'action'
12219
+ : rawTargets.some((t) => t?.startsWith('row:')) ? 'row'
12220
+ : rawTargets.some((t) => t?.startsWith('column:')) ? 'column'
12221
+ : 'field');
11462
12222
  const targets = rawTargets.map((t) => stripPrefix(t));
11463
12223
  return { targetType, targets };
11464
12224
  }
11465
12225
  function stripPrefix(value) {
12226
+ if (isScopedSectionHeaderActionTarget(value) || isUnscopedSectionHeaderActionTarget(value)) {
12227
+ return value;
12228
+ }
11466
12229
  if (value?.startsWith('section:'))
11467
12230
  return value.substring('section:'.length);
11468
12231
  if (value?.startsWith('action:'))
@@ -11473,6 +12236,12 @@ function stripPrefix(value) {
11473
12236
  return value.substring('column:'.length);
11474
12237
  return value;
11475
12238
  }
12239
+ function isScopedSectionHeaderActionTarget(value) {
12240
+ return !!value && value.startsWith('section:') && value.includes(':header-action:');
12241
+ }
12242
+ function isUnscopedSectionHeaderActionTarget(value) {
12243
+ return !!value && value.startsWith('header-action:');
12244
+ }
11476
12245
  function inferPropertiesFromContext(context) {
11477
12246
  switch (context) {
11478
12247
  case 'visibility':
@@ -11844,6 +12613,12 @@ const dialogFieldsWithModeDependency = dialogFieldsWithoutMessage.map((field) =>
11844
12613
  hint: field.hint ?? 'Disponível quando "Usar diálogo" estiver ativo.',
11845
12614
  }));
11846
12615
  const GLOBAL_ACTION_UI_SCHEMAS = [
12616
+ {
12617
+ id: 'surface.open',
12618
+ label: 'Abrir Surface',
12619
+ fields: [],
12620
+ editorMode: 'surface-open',
12621
+ },
11847
12622
  {
11848
12623
  id: 'navigate',
11849
12624
  label: 'Navegar',
@@ -12051,41 +12826,1209 @@ function getGlobalActionUiSchema(id) {
12051
12826
  return GLOBAL_ACTION_UI_SCHEMAS.find((schema) => schema.id === id);
12052
12827
  }
12053
12828
 
12054
- /**
12055
- * Catálogo de capacidades genéricas de FieldMetadata para uso da IA.
12056
- * Baseado em projects/praxis-core/src/lib/models/component-metadata.interface.ts
12057
- */
12058
- const ENUMS$1 = {
12059
- fieldControlType: Object.values(FieldControlType),
12060
- fieldDataType: Object.values(FieldDataType),
12061
- componentContext: ['form', 'filter', 'table', 'dialog', 'standalone'],
12062
- iconPosition: ['start', 'end'],
12063
- iconSize: ['small', 'medium', 'large'],
12064
- validationMode: ['immediate', 'blur', 'submit'],
12065
- validatorTrigger: ['change', 'blur', 'submit', 'immediate'],
12066
- validatorErrorPosition: ['bottom', 'top', 'tooltip'],
12067
- dependencyMergeStrategy: ['replace', 'merge'],
12068
- dependencyLoadOnChange: ['respectLoadOn', 'immediate', 'manual'],
12069
- textTransformApply: ['displayOnly', 'saveOnly', 'both'],
12070
- visibleIn: ['form', 'filter', 'table', 'dialog'],
12071
- appearance: ['fill', 'outline'],
12072
- color: ['primary', 'accent', 'warn'],
12073
- floatLabel: ['auto', 'always', 'never'],
12074
- hintPosition: ['start', 'end'],
12075
- align: ['start', 'center', 'end', 'stretch'],
12076
- sectionTitleStyle: ['titleLarge', 'titleMedium', 'titleSmall', 'headlineSmall'],
12077
- sectionDescriptionStyle: ['bodyLarge', 'bodyMedium', 'bodySmall'],
12078
- };
12079
- const FIELD_METADATA_CAPABILITIES = {
12080
- version: 'v1.3',
12081
- enums: ENUMS$1,
12082
- notes: [
12083
- 'Este catálogo define as capacidades genéricas de FieldMetadata.',
12084
- 'O host típico para estes metadados é FormConfig (via fieldMetadata[]) ou FormLayout.',
12085
- 'Detalhes específicos de cada controle (ex: opções de datepicker, máscaras específicas) devem ser consultados nos catálogos de microcomponentes.',
12086
- 'POLICY: Arrays e objetos (ex: options, validators) devem sofrer merge/append, nunca substituição completa sem confirmação.',
12087
- 'Funções (conditionalRequired, transforms) não são serializáveis. O LLM não deve gerar código de função, apenas expressões string se suportado pelo interpretador, ou solicitar configuração manual.',
12088
- ],
12829
+ const SURFACE_OPEN_PRESETS = [
12830
+ {
12831
+ id: 'praxis-dynamic-form',
12832
+ label: 'Formulário',
12833
+ description: 'Abre um formulário dinâmico com resourcePath, formId e resourceId.',
12834
+ payload: {
12835
+ presentation: 'drawer',
12836
+ widget: {
12837
+ id: 'praxis-dynamic-form',
12838
+ bindingOrder: ['resourcePath', 'formId', 'resourceId'],
12839
+ inputs: {
12840
+ resourcePath: '',
12841
+ formId: '',
12842
+ mode: 'view',
12843
+ },
12844
+ },
12845
+ bindings: [],
12846
+ },
12847
+ },
12848
+ {
12849
+ id: 'praxis-table',
12850
+ label: 'Tabela',
12851
+ description: 'Abre uma tabela filtrável usando inputs declarativos e queryContext.',
12852
+ payload: {
12853
+ presentation: 'drawer',
12854
+ widget: {
12855
+ id: 'praxis-table',
12856
+ bindingOrder: ['resourcePath', 'tableId', 'queryContext'],
12857
+ inputs: {
12858
+ resourcePath: '',
12859
+ tableId: '',
12860
+ queryContext: {
12861
+ filters: {},
12862
+ },
12863
+ },
12864
+ },
12865
+ bindings: [],
12866
+ },
12867
+ },
12868
+ {
12869
+ id: 'praxis-list',
12870
+ label: 'Lista',
12871
+ description: 'Abre uma lista com config/listId e bindings configuráveis.',
12872
+ payload: {
12873
+ presentation: 'drawer',
12874
+ widget: {
12875
+ id: 'praxis-list',
12876
+ bindingOrder: ['listId', 'config'],
12877
+ inputs: {
12878
+ listId: '',
12879
+ config: {},
12880
+ },
12881
+ },
12882
+ bindings: [],
12883
+ },
12884
+ },
12885
+ {
12886
+ id: 'praxis-crud',
12887
+ label: 'CRUD',
12888
+ description: 'Abre a surface de CRUD com inputs mínimos e bindings configuráveis.',
12889
+ payload: {
12890
+ presentation: 'drawer',
12891
+ widget: {
12892
+ id: 'praxis-crud',
12893
+ bindingOrder: ['resourcePath', 'formId', 'resourceId'],
12894
+ inputs: {
12895
+ resourcePath: '',
12896
+ formId: '',
12897
+ },
12898
+ },
12899
+ bindings: [],
12900
+ },
12901
+ },
12902
+ ];
12903
+
12904
+ class SurfaceOpenActionEditorComponent {
12905
+ value;
12906
+ hostKind;
12907
+ valueChange = new EventEmitter();
12908
+ draft = this.createDefaultPayload();
12909
+ inputJsonDrafts = {};
12910
+ inputErrors = {};
12911
+ contextDraft = '';
12912
+ contextError = '';
12913
+ registry = inject(ComponentMetadataRegistry);
12914
+ i18n = inject(PraxisI18nService);
12915
+ static nextInstanceId = 0;
12916
+ instanceId = ++SurfaceOpenActionEditorComponent.nextInstanceId;
12917
+ sourceSuggestionsListId = `surface-binding-source-suggestions-${this.instanceId}`;
12918
+ targetSuggestionsListId = `surface-binding-target-suggestions-${this.instanceId}`;
12919
+ get availablePresets() {
12920
+ return SURFACE_OPEN_PRESETS;
12921
+ }
12922
+ get componentOptions() {
12923
+ return this.registry
12924
+ .getAll()
12925
+ .slice()
12926
+ .sort((a, b) => String(a.friendlyName || a.id).localeCompare(String(b.friendlyName || b.id)));
12927
+ }
12928
+ get selectedComponentMeta() {
12929
+ return this.draft.widget?.id ? this.registry.get(this.draft.widget.id) : undefined;
12930
+ }
12931
+ get selectedInputs() {
12932
+ return this.selectedComponentMeta?.inputs || [];
12933
+ }
12934
+ get bindingOrderText() {
12935
+ return (this.draft.widget.bindingOrder || []).join(', ');
12936
+ }
12937
+ get bindingSourceSuggestions() {
12938
+ const generic = [
12939
+ 'payload.id',
12940
+ 'payload.resourceId',
12941
+ 'payload.row.id',
12942
+ 'payload.item.id',
12943
+ 'payload.formData.id',
12944
+ 'context.resourceId',
12945
+ 'pageContext.resourceId',
12946
+ ];
12947
+ const byHost = {
12948
+ table: ['payload.row.id', 'payload.row.departmentId', 'payload.selection.ids', 'runtime.row.id'],
12949
+ form: ['payload.resourceId', 'payload.formData.customerId', 'runtime.formData.id'],
12950
+ list: ['payload.item.id', 'payload.item.status', 'payload.selection.ids', 'runtime.item.id'],
12951
+ page: ['pageContext.filters.status', 'pageContext.selection.ids', 'context.pageState.activeId'],
12952
+ };
12953
+ return this.uniqueStrings([...(byHost[this.hostKind || ''] || []), ...generic]);
12954
+ }
12955
+ get bindingTargetSuggestions() {
12956
+ const metaTargets = this.selectedInputs.map((input) => `widget.inputs.${input.name}`);
12957
+ const commonTargets = [
12958
+ 'widget.inputs.resourcePath',
12959
+ 'widget.inputs.formId',
12960
+ 'widget.inputs.resourceId',
12961
+ 'widget.inputs.tableId',
12962
+ 'widget.inputs.listId',
12963
+ 'widget.inputs.queryContext',
12964
+ 'widget.inputs.queryContext.filters',
12965
+ 'widget.inputs.filterCriteria',
12966
+ ];
12967
+ return this.uniqueStrings([...metaTargets, ...commonTargets]);
12968
+ }
12969
+ get bindingSourceSuggestionsPreview() {
12970
+ return this.bindingSourceSuggestions.slice(0, 5).join(', ');
12971
+ }
12972
+ get bindingTargetSuggestionsPreview() {
12973
+ return this.bindingTargetSuggestions.slice(0, 5).join(', ');
12974
+ }
12975
+ ngOnChanges(changes) {
12976
+ if (changes['value']) {
12977
+ this.draft = this.normalizePayload(this.value);
12978
+ this.refreshJsonDrafts();
12979
+ }
12980
+ }
12981
+ updatePresentation(value) {
12982
+ this.draft.presentation = value === 'drawer' ? 'drawer' : 'modal';
12983
+ this.emitDraft();
12984
+ }
12985
+ updateTextField(field, value) {
12986
+ this.assignTextField(field, value);
12987
+ this.emitDraft();
12988
+ }
12989
+ updateSizeField(field, value) {
12990
+ const text = String(value || '').trim();
12991
+ if (!text) {
12992
+ if (this.draft.size) {
12993
+ delete this.draft.size[field];
12994
+ if (!Object.keys(this.draft.size).length) {
12995
+ delete this.draft.size;
12996
+ }
12997
+ }
12998
+ }
12999
+ else {
13000
+ this.draft.size = { ...(this.draft.size || {}), [field]: text };
13001
+ }
13002
+ this.emitDraft();
13003
+ }
13004
+ updateWidgetId(componentId) {
13005
+ const nextId = String(componentId || '').trim();
13006
+ const previousInputs = { ...(this.draft.widget.inputs || {}) };
13007
+ this.draft.widget.id = nextId;
13008
+ this.draft.widget.inputs = this.reconcileInputsForComponent(nextId, previousInputs);
13009
+ this.refreshJsonDrafts();
13010
+ this.emitDraft();
13011
+ }
13012
+ updateBindingOrder(value) {
13013
+ const items = String(value || '')
13014
+ .split(',')
13015
+ .map((item) => item.trim())
13016
+ .filter(Boolean);
13017
+ if (items.length) {
13018
+ this.draft.widget.bindingOrder = items;
13019
+ }
13020
+ else {
13021
+ delete this.draft.widget.bindingOrder;
13022
+ }
13023
+ this.emitDraft();
13024
+ }
13025
+ applyPreset(preset) {
13026
+ this.draft = this.normalizePayload(preset.payload);
13027
+ this.refreshJsonDrafts();
13028
+ this.emitDraft();
13029
+ }
13030
+ isBooleanType(type) {
13031
+ return /^(bool|boolean)$/i.test(String(type || ''));
13032
+ }
13033
+ isNumberType(type) {
13034
+ return /^(number|int|float|double)$/i.test(String(type || ''));
13035
+ }
13036
+ isJsonType(type) {
13037
+ return /(json|object|record|map|array|\[\]|criteria|config|input|params)/i.test(String(type || ''));
13038
+ }
13039
+ getBooleanInputValue(name) {
13040
+ return Boolean(this.draft.widget.inputs?.[name]);
13041
+ }
13042
+ getScalarInputValue(name) {
13043
+ return this.draft.widget.inputs?.[name] ?? '';
13044
+ }
13045
+ getJsonInputDraft(name) {
13046
+ if (!(name in this.inputJsonDrafts)) {
13047
+ this.inputJsonDrafts[name] = this.stringifyJson(this.draft.widget.inputs?.[name]);
13048
+ }
13049
+ return this.inputJsonDrafts[name];
13050
+ }
13051
+ scalarPlaceholder(input) {
13052
+ if (input.default !== undefined && input.default !== null && input.default !== '') {
13053
+ return String(input.default);
13054
+ }
13055
+ return '';
13056
+ }
13057
+ jsonPlaceholder(input) {
13058
+ if (input.default !== undefined && input.default !== null && input.default !== '') {
13059
+ return this.stringifyJson(input.default);
13060
+ }
13061
+ return '{ }';
13062
+ }
13063
+ updateBooleanInput(name, value) {
13064
+ this.ensureWidgetInputs();
13065
+ this.draft.widget.inputs[name] = Boolean(value);
13066
+ this.emitDraft();
13067
+ }
13068
+ updateScalarInput(name, type, value) {
13069
+ this.ensureWidgetInputs();
13070
+ const text = String(value ?? '');
13071
+ if (!text.trim()) {
13072
+ delete this.draft.widget.inputs[name];
13073
+ }
13074
+ else if (this.isNumberType(type)) {
13075
+ const parsed = Number(text);
13076
+ this.draft.widget.inputs[name] = Number.isFinite(parsed) ? parsed : text;
13077
+ }
13078
+ else {
13079
+ this.draft.widget.inputs[name] = value;
13080
+ }
13081
+ this.emitDraft();
13082
+ }
13083
+ updateJsonInput(name, value) {
13084
+ const raw = String(value || '');
13085
+ this.inputJsonDrafts[name] = raw;
13086
+ if (!raw.trim()) {
13087
+ delete this.inputErrors[name];
13088
+ if (this.draft.widget.inputs) {
13089
+ delete this.draft.widget.inputs[name];
13090
+ }
13091
+ this.emitDraft();
13092
+ return;
13093
+ }
13094
+ try {
13095
+ const parsed = JSON.parse(raw);
13096
+ delete this.inputErrors[name];
13097
+ this.ensureWidgetInputs();
13098
+ this.draft.widget.inputs[name] = parsed;
13099
+ this.emitDraft();
13100
+ }
13101
+ catch {
13102
+ this.inputErrors[name] = this.t('editor.validation.invalidJson', 'JSON inválido.');
13103
+ }
13104
+ }
13105
+ addBinding() {
13106
+ const bindings = [...(this.draft.bindings || [])];
13107
+ bindings.push({
13108
+ from: '',
13109
+ to: this.selectedInputs[0]
13110
+ ? `widget.inputs.${this.selectedInputs[0].name}`
13111
+ : 'widget.inputs.resourceId',
13112
+ mode: 'path',
13113
+ });
13114
+ this.draft.bindings = bindings;
13115
+ this.emitDraft();
13116
+ }
13117
+ updateBinding(index, field, value) {
13118
+ const bindings = [...(this.draft.bindings || [])];
13119
+ const current = { ...(bindings[index] || { to: 'widget.inputs.resourceId', mode: 'path' }) };
13120
+ if (field === 'mode') {
13121
+ current.mode = value || 'path';
13122
+ if (current.mode !== 'constant') {
13123
+ delete current.value;
13124
+ }
13125
+ else if (current.value === undefined) {
13126
+ current.value = '';
13127
+ }
13128
+ }
13129
+ else if (field === 'value') {
13130
+ current.value = value;
13131
+ }
13132
+ else if (field === 'from') {
13133
+ current.from = String(value || '').trim();
13134
+ }
13135
+ else if (field === 'to') {
13136
+ current.to = String(value || '').trim();
13137
+ }
13138
+ bindings[index] = current;
13139
+ this.draft.bindings = bindings;
13140
+ this.emitDraft();
13141
+ }
13142
+ removeBinding(index) {
13143
+ const bindings = [...(this.draft.bindings || [])];
13144
+ bindings.splice(index, 1);
13145
+ this.draft.bindings = bindings.length ? bindings : undefined;
13146
+ this.emitDraft();
13147
+ }
13148
+ updateContext(value) {
13149
+ const raw = String(value || '');
13150
+ this.contextDraft = raw;
13151
+ if (!raw.trim()) {
13152
+ this.contextError = '';
13153
+ delete this.draft.context;
13154
+ this.emitDraft();
13155
+ return;
13156
+ }
13157
+ try {
13158
+ const parsed = JSON.parse(raw);
13159
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
13160
+ this.contextError = this.t('editor.validation.objectJson', 'Informe um objeto JSON.');
13161
+ return;
13162
+ }
13163
+ this.contextError = '';
13164
+ this.draft.context = parsed;
13165
+ this.emitDraft();
13166
+ }
13167
+ catch {
13168
+ this.contextError = this.t('editor.validation.invalidJson', 'JSON inválido.');
13169
+ }
13170
+ }
13171
+ presetLabel(preset) {
13172
+ return this.t(`preset.${preset.id}.label`, preset.label);
13173
+ }
13174
+ presetDescription(preset) {
13175
+ return this.t(`preset.${preset.id}.description`, preset.description);
13176
+ }
13177
+ t(key, fallback, params) {
13178
+ return this.i18n.t(key, params, fallback, SURFACE_OPEN_I18N_NAMESPACE);
13179
+ }
13180
+ refreshJsonDrafts() {
13181
+ this.inputJsonDrafts = {};
13182
+ this.inputErrors = {};
13183
+ for (const input of this.selectedInputs) {
13184
+ if (this.isJsonType(input.type)) {
13185
+ this.inputJsonDrafts[input.name] = this.stringifyJson(this.draft.widget.inputs?.[input.name]);
13186
+ }
13187
+ }
13188
+ this.contextDraft = this.stringifyJson(this.draft.context);
13189
+ this.contextError = '';
13190
+ }
13191
+ emitDraft() {
13192
+ const normalized = this.normalizePayload(this.draft);
13193
+ this.draft = normalized;
13194
+ this.valueChange.emit(this.clone(normalized));
13195
+ }
13196
+ ensureWidgetInputs() {
13197
+ this.draft.widget.inputs = { ...(this.draft.widget.inputs || {}) };
13198
+ }
13199
+ reconcileInputsForComponent(componentId, previousInputs) {
13200
+ const meta = componentId ? this.registry.get(componentId) : undefined;
13201
+ if (!meta?.inputs?.length) {
13202
+ return {};
13203
+ }
13204
+ const nextInputs = {};
13205
+ for (const input of meta.inputs) {
13206
+ if (Object.prototype.hasOwnProperty.call(previousInputs, input.name)) {
13207
+ nextInputs[input.name] = previousInputs[input.name];
13208
+ continue;
13209
+ }
13210
+ if (input.default !== undefined) {
13211
+ nextInputs[input.name] = this.clone(input.default);
13212
+ }
13213
+ }
13214
+ return nextInputs;
13215
+ }
13216
+ assignTextField(field, value) {
13217
+ const text = String(value || '').trim();
13218
+ if (text) {
13219
+ this.draft[field] = text;
13220
+ }
13221
+ else {
13222
+ delete this.draft[field];
13223
+ }
13224
+ }
13225
+ normalizePayload(value) {
13226
+ const clone = this.clone(value || this.createDefaultPayload());
13227
+ const normalized = {
13228
+ presentation: clone.presentation === 'drawer' ? 'drawer' : 'modal',
13229
+ widget: {
13230
+ id: String(clone.widget?.id || '').trim(),
13231
+ inputs: { ...(clone.widget?.inputs || {}) },
13232
+ },
13233
+ };
13234
+ if (clone.title)
13235
+ normalized.title = clone.title;
13236
+ if (clone.subtitle)
13237
+ normalized.subtitle = clone.subtitle;
13238
+ if (clone.icon)
13239
+ normalized.icon = clone.icon;
13240
+ if (clone.size && Object.keys(clone.size).length) {
13241
+ normalized.size = { ...clone.size };
13242
+ }
13243
+ if (clone.widget?.bindingOrder?.length) {
13244
+ normalized.widget.bindingOrder = clone.widget.bindingOrder
13245
+ .map((item) => String(item || '').trim())
13246
+ .filter(Boolean);
13247
+ }
13248
+ if (clone.bindings?.length) {
13249
+ normalized.bindings = clone.bindings
13250
+ .map((binding) => ({
13251
+ from: binding.from ? String(binding.from).trim() : undefined,
13252
+ to: String(binding.to || '').trim(),
13253
+ mode: binding.mode || 'path',
13254
+ value: binding.value,
13255
+ }))
13256
+ .filter((binding) => !!binding.to);
13257
+ }
13258
+ if (clone.context && typeof clone.context === 'object' && !Array.isArray(clone.context)) {
13259
+ normalized.context = { ...clone.context };
13260
+ }
13261
+ return normalized;
13262
+ }
13263
+ createDefaultPayload() {
13264
+ return {
13265
+ presentation: 'modal',
13266
+ widget: {
13267
+ id: '',
13268
+ inputs: {},
13269
+ },
13270
+ bindings: [],
13271
+ };
13272
+ }
13273
+ stringifyJson(value) {
13274
+ if (value === undefined || value === null || value === '')
13275
+ return '';
13276
+ if (typeof value === 'string')
13277
+ return value;
13278
+ try {
13279
+ return JSON.stringify(value, null, 2);
13280
+ }
13281
+ catch {
13282
+ return '';
13283
+ }
13284
+ }
13285
+ clone(value) {
13286
+ return value == null ? value : JSON.parse(JSON.stringify(value));
13287
+ }
13288
+ uniqueStrings(values) {
13289
+ return Array.from(new Set(values
13290
+ .map((value) => String(value || '').trim())
13291
+ .filter(Boolean)));
13292
+ }
13293
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SurfaceOpenActionEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13294
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: SurfaceOpenActionEditorComponent, isStandalone: true, selector: "praxis-surface-open-action-editor", inputs: { value: "value", hostKind: "hostKind" }, outputs: { valueChange: "valueChange" }, providers: [providePraxisI18nConfig(SURFACE_OPEN_I18N_CONFIG)], usesOnChanges: true, ngImport: i0, template: `
13295
+ <div class="surface-editor">
13296
+ <div class="surface-section">
13297
+ <div class="surface-section-title">{{ t('editor.presets.title', 'Presets') }}</div>
13298
+ <div class="surface-preset-row">
13299
+ @for (preset of availablePresets; track preset.id) {
13300
+ <button
13301
+ mat-stroked-button
13302
+ type="button"
13303
+ (click)="applyPreset(preset)"
13304
+ [matTooltip]="presetDescription(preset)"
13305
+ >
13306
+ {{ presetLabel(preset) }}
13307
+ </button>
13308
+ }
13309
+ </div>
13310
+ <div class="surface-empty">
13311
+ {{
13312
+ t(
13313
+ 'editor.presets.help',
13314
+ 'Aplique um preset para começar com inputs mínimos e depois configure bindings conforme a origem da action.'
13315
+ )
13316
+ }}
13317
+ </div>
13318
+ </div>
13319
+
13320
+ <div class="surface-section">
13321
+ <div class="surface-section-title">{{ t('editor.surface.title', 'Surface') }}</div>
13322
+ <div class="surface-grid">
13323
+ <mat-form-field appearance="outline">
13324
+ <mat-label>{{ t('editor.surface.presentation', 'Apresentação') }}</mat-label>
13325
+ <mat-select
13326
+ [ngModel]="draft.presentation"
13327
+ (ngModelChange)="updatePresentation($event)"
13328
+ [ngModelOptions]="{ standalone: true }"
13329
+ >
13330
+ <mat-option value="modal">{{ t('editor.surface.presentation.modal', 'Modal') }}</mat-option>
13331
+ <mat-option value="drawer">{{ t('editor.surface.presentation.drawer', 'Drawer') }}</mat-option>
13332
+ </mat-select>
13333
+ </mat-form-field>
13334
+
13335
+ <mat-form-field appearance="outline">
13336
+ <mat-label>{{ t('editor.surface.titleLabel', 'Título') }}</mat-label>
13337
+ <input
13338
+ matInput
13339
+ [ngModel]="draft.title || ''"
13340
+ (ngModelChange)="updateTextField('title', $event)"
13341
+ [ngModelOptions]="{ standalone: true }"
13342
+ />
13343
+ </mat-form-field>
13344
+
13345
+ <mat-form-field appearance="outline">
13346
+ <mat-label>{{ t('editor.surface.subtitleLabel', 'Subtítulo') }}</mat-label>
13347
+ <input
13348
+ matInput
13349
+ [ngModel]="draft.subtitle || ''"
13350
+ (ngModelChange)="updateTextField('subtitle', $event)"
13351
+ [ngModelOptions]="{ standalone: true }"
13352
+ />
13353
+ </mat-form-field>
13354
+
13355
+ <mat-form-field appearance="outline">
13356
+ <mat-label>{{ t('editor.surface.iconLabel', 'Ícone') }}</mat-label>
13357
+ <input
13358
+ matInput
13359
+ [ngModel]="draft.icon || ''"
13360
+ (ngModelChange)="updateTextField('icon', $event)"
13361
+ [ngModelOptions]="{ standalone: true }"
13362
+ placeholder="open_in_new"
13363
+ />
13364
+ </mat-form-field>
13365
+
13366
+ <mat-form-field appearance="outline">
13367
+ <mat-label>{{ t('editor.surface.widthLabel', 'Largura') }}</mat-label>
13368
+ <input
13369
+ matInput
13370
+ [ngModel]="draft.size?.width || ''"
13371
+ (ngModelChange)="updateSizeField('width', $event)"
13372
+ [ngModelOptions]="{ standalone: true }"
13373
+ placeholder="720px"
13374
+ />
13375
+ </mat-form-field>
13376
+
13377
+ <mat-form-field appearance="outline">
13378
+ <mat-label>{{ t('editor.surface.heightLabel', 'Altura') }}</mat-label>
13379
+ <input
13380
+ matInput
13381
+ [ngModel]="draft.size?.height || ''"
13382
+ (ngModelChange)="updateSizeField('height', $event)"
13383
+ [ngModelOptions]="{ standalone: true }"
13384
+ placeholder="80vh"
13385
+ />
13386
+ </mat-form-field>
13387
+
13388
+ <mat-form-field appearance="outline">
13389
+ <mat-label>{{ t('editor.surface.minWidthLabel', 'Largura mínima') }}</mat-label>
13390
+ <input
13391
+ matInput
13392
+ [ngModel]="draft.size?.minWidth || ''"
13393
+ (ngModelChange)="updateSizeField('minWidth', $event)"
13394
+ [ngModelOptions]="{ standalone: true }"
13395
+ placeholder="360px"
13396
+ />
13397
+ </mat-form-field>
13398
+
13399
+ <mat-form-field appearance="outline">
13400
+ <mat-label>{{ t('editor.surface.maxWidthLabel', 'Largura máxima') }}</mat-label>
13401
+ <input
13402
+ matInput
13403
+ [ngModel]="draft.size?.maxWidth || ''"
13404
+ (ngModelChange)="updateSizeField('maxWidth', $event)"
13405
+ [ngModelOptions]="{ standalone: true }"
13406
+ placeholder="90vw"
13407
+ />
13408
+ </mat-form-field>
13409
+ </div>
13410
+ </div>
13411
+
13412
+ <div class="surface-section">
13413
+ <div class="surface-section-title">{{ t('editor.component.title', 'Componente') }}</div>
13414
+ <div class="surface-grid">
13415
+ <mat-form-field appearance="outline" class="surface-span-2">
13416
+ <mat-label>{{ t('editor.component.select', 'Componente registrado') }}</mat-label>
13417
+ <mat-select
13418
+ [ngModel]="draft.widget.id"
13419
+ (ngModelChange)="updateWidgetId($event)"
13420
+ [ngModelOptions]="{ standalone: true }"
13421
+ >
13422
+ <mat-option value="">{{ t('editor.component.selectPlaceholder', '-- Selecionar --') }}</mat-option>
13423
+ @for (option of componentOptions; track option.id) {
13424
+ <mat-option [value]="option.id">
13425
+ {{ option.friendlyName || option.id }}
13426
+ </mat-option>
13427
+ }
13428
+ </mat-select>
13429
+ </mat-form-field>
13430
+
13431
+ @if (selectedComponentMeta) {
13432
+ <div class="surface-component-meta surface-span-2">
13433
+ <div class="surface-component-title">
13434
+ {{ selectedComponentMeta.friendlyName || selectedComponentMeta.id }}
13435
+ </div>
13436
+ @if (selectedComponentMeta.description) {
13437
+ <div class="surface-component-description">
13438
+ {{ selectedComponentMeta.description }}
13439
+ </div>
13440
+ }
13441
+ </div>
13442
+ }
13443
+
13444
+ <mat-form-field appearance="outline" class="surface-span-2">
13445
+ <mat-label>{{ t('editor.component.bindingOrder', 'bindingOrder (CSV)') }}</mat-label>
13446
+ <input
13447
+ matInput
13448
+ [ngModel]="bindingOrderText"
13449
+ (ngModelChange)="updateBindingOrder($event)"
13450
+ [ngModelOptions]="{ standalone: true }"
13451
+ [placeholder]="t('editor.component.bindingOrderPlaceholder', 'resourcePath, formId, resourceId')"
13452
+ />
13453
+ </mat-form-field>
13454
+ </div>
13455
+ </div>
13456
+
13457
+ <div class="surface-section">
13458
+ <div class="surface-section-title">{{ t('editor.inputs.title', 'Inputs') }}</div>
13459
+ @if (selectedInputs.length) {
13460
+ <div class="surface-grid">
13461
+ @for (input of selectedInputs; track input.name) {
13462
+ @if (isBooleanType(input.type)) {
13463
+ <mat-slide-toggle
13464
+ [ngModel]="getBooleanInputValue(input.name)"
13465
+ (ngModelChange)="updateBooleanInput(input.name, $event)"
13466
+ [ngModelOptions]="{ standalone: true }"
13467
+ >
13468
+ {{ input.label || input.name }}
13469
+ </mat-slide-toggle>
13470
+ } @else if (isJsonType(input.type)) {
13471
+ <mat-form-field appearance="outline" class="surface-span-2">
13472
+ <mat-label>{{ input.label || input.name }}</mat-label>
13473
+ <textarea
13474
+ matInput
13475
+ rows="4"
13476
+ [ngModel]="getJsonInputDraft(input.name)"
13477
+ (ngModelChange)="updateJsonInput(input.name, $event)"
13478
+ [ngModelOptions]="{ standalone: true }"
13479
+ [placeholder]="jsonPlaceholder(input)"
13480
+ ></textarea>
13481
+ @if (input.description) {
13482
+ <mat-hint>{{ input.description }}</mat-hint>
13483
+ }
13484
+ @if (inputErrors[input.name]) {
13485
+ <mat-error>{{ inputErrors[input.name] }}</mat-error>
13486
+ }
13487
+ </mat-form-field>
13488
+ } @else {
13489
+ <mat-form-field appearance="outline">
13490
+ <mat-label>{{ input.label || input.name }}</mat-label>
13491
+ <input
13492
+ matInput
13493
+ [type]="isNumberType(input.type) ? 'number' : 'text'"
13494
+ [ngModel]="getScalarInputValue(input.name)"
13495
+ (ngModelChange)="updateScalarInput(input.name, input.type, $event)"
13496
+ [ngModelOptions]="{ standalone: true }"
13497
+ [placeholder]="scalarPlaceholder(input)"
13498
+ />
13499
+ @if (input.description) {
13500
+ <mat-hint>{{ input.description }}</mat-hint>
13501
+ }
13502
+ </mat-form-field>
13503
+ }
13504
+ }
13505
+ </div>
13506
+ } @else {
13507
+ <div class="surface-empty">
13508
+ {{ t('editor.inputs.empty', 'Selecione um componente registrado para configurar inputs públicos.') }}
13509
+ </div>
13510
+ }
13511
+ </div>
13512
+
13513
+ <div class="surface-section">
13514
+ <div class="surface-section-header">
13515
+ <div class="surface-section-title">{{ t('editor.bindings.title', 'Bindings') }}</div>
13516
+ <button mat-stroked-button type="button" (click)="addBinding()">
13517
+ <mat-icon>add</mat-icon>
13518
+ {{ t('editor.bindings.add', 'Binding') }}
13519
+ </button>
13520
+ </div>
13521
+ @if (draft.bindings?.length) {
13522
+ <div class="surface-bindings">
13523
+ @for (binding of draft.bindings || []; track $index) {
13524
+ <div class="surface-binding-row">
13525
+ <mat-form-field appearance="outline">
13526
+ <mat-label>{{ t('editor.bindings.source', 'Origem') }}</mat-label>
13527
+ <input
13528
+ matInput
13529
+ [ngModel]="binding.from || ''"
13530
+ (ngModelChange)="updateBinding($index, 'from', $event)"
13531
+ [ngModelOptions]="{ standalone: true }"
13532
+ [attr.list]="sourceSuggestionsListId"
13533
+ placeholder="payload.row.id"
13534
+ />
13535
+ </mat-form-field>
13536
+
13537
+ <mat-form-field appearance="outline">
13538
+ <mat-label>{{ t('editor.bindings.target', 'Destino') }}</mat-label>
13539
+ <input
13540
+ matInput
13541
+ [ngModel]="binding.to"
13542
+ (ngModelChange)="updateBinding($index, 'to', $event)"
13543
+ [ngModelOptions]="{ standalone: true }"
13544
+ [attr.list]="targetSuggestionsListId"
13545
+ placeholder="widget.inputs.resourceId"
13546
+ />
13547
+ </mat-form-field>
13548
+
13549
+ <mat-form-field appearance="outline">
13550
+ <mat-label>{{ t('editor.bindings.mode', 'Modo') }}</mat-label>
13551
+ <mat-select
13552
+ [ngModel]="binding.mode || 'path'"
13553
+ (ngModelChange)="updateBinding($index, 'mode', $event)"
13554
+ [ngModelOptions]="{ standalone: true }"
13555
+ >
13556
+ <mat-option value="path">path</mat-option>
13557
+ <mat-option value="template">template</mat-option>
13558
+ <mat-option value="constant">constant</mat-option>
13559
+ </mat-select>
13560
+ </mat-form-field>
13561
+
13562
+ @if ((binding.mode || 'path') === 'constant') {
13563
+ <mat-form-field appearance="outline">
13564
+ <mat-label>{{ t('editor.bindings.constantValue', 'Valor constante') }}</mat-label>
13565
+ <input
13566
+ matInput
13567
+ [ngModel]="binding.value ?? ''"
13568
+ (ngModelChange)="updateBinding($index, 'value', $event)"
13569
+ [ngModelOptions]="{ standalone: true }"
13570
+ />
13571
+ </mat-form-field>
13572
+ }
13573
+
13574
+ <button
13575
+ mat-icon-button
13576
+ color="warn"
13577
+ type="button"
13578
+ (click)="removeBinding($index)"
13579
+ [attr.aria-label]="t('editor.bindings.remove', 'Remover binding')"
13580
+ >
13581
+ <mat-icon>delete</mat-icon>
13582
+ </button>
13583
+ </div>
13584
+ }
13585
+ </div>
13586
+ } @else {
13587
+ <div class="surface-empty">
13588
+ {{ t('editor.bindings.empty', 'Nenhum binding configurado ainda.') }}
13589
+ </div>
13590
+ }
13591
+ <div class="surface-empty">
13592
+ @if (bindingSourceSuggestionsPreview) {
13593
+ <div>
13594
+ {{ t('editor.bindings.suggestedSources', 'Origens sugeridas: {{value}}', { value: bindingSourceSuggestionsPreview }) }}
13595
+ </div>
13596
+ }
13597
+ @if (bindingTargetSuggestionsPreview) {
13598
+ <div>
13599
+ {{ t('editor.bindings.suggestedTargets', 'Destinos sugeridos: {{value}}', { value: bindingTargetSuggestionsPreview }) }}
13600
+ </div>
13601
+ }
13602
+ </div>
13603
+ <datalist [id]="sourceSuggestionsListId">
13604
+ @for (suggestion of bindingSourceSuggestions; track suggestion) {
13605
+ <option [value]="suggestion"></option>
13606
+ }
13607
+ </datalist>
13608
+ <datalist [id]="targetSuggestionsListId">
13609
+ @for (suggestion of bindingTargetSuggestions; track suggestion) {
13610
+ <option [value]="suggestion"></option>
13611
+ }
13612
+ </datalist>
13613
+ </div>
13614
+
13615
+ <div class="surface-section">
13616
+ <div class="surface-section-title">{{ t('editor.context.title', 'Contexto extra') }}</div>
13617
+ <mat-form-field appearance="outline" class="surface-span-all">
13618
+ <mat-label>{{ t('editor.context.label', 'context (JSON)') }}</mat-label>
13619
+ <textarea
13620
+ matInput
13621
+ rows="4"
13622
+ [ngModel]="contextDraft"
13623
+ (ngModelChange)="updateContext($event)"
13624
+ [ngModelOptions]="{ standalone: true }"
13625
+ placeholder="{ }"
13626
+ ></textarea>
13627
+ <mat-hint>{{ t('editor.context.hint', 'Opcional. Mesclado ao contexto da action no runtime.') }}</mat-hint>
13628
+ @if (contextError) {
13629
+ <mat-error>{{ contextError }}</mat-error>
13630
+ }
13631
+ </mat-form-field>
13632
+ </div>
13633
+ </div>
13634
+ `, isInline: true, styles: [".surface-editor{display:grid;gap:16px}.surface-section{padding:12px 14px;border:1px dashed var(--md-sys-color-outline-variant, rgba(0, 0, 0, .18));border-radius:10px;background:var(--md-sys-color-surface-container-lowest, #fff)}.surface-section-header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}.surface-section-title{font-size:12px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant, #5f6368);margin-bottom:10px}.surface-section-header .surface-section-title{margin-bottom:0}.surface-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px 16px}.surface-preset-row{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}.surface-span-2{grid-column:span 2}.surface-span-all{width:100%}.surface-component-meta{display:grid;gap:4px}.surface-component-title{font-weight:600}.surface-component-description,.surface-empty{color:var(--md-sys-color-on-surface-variant, #5f6368);font-size:12px}.surface-bindings{display:grid;gap:12px}.surface-binding-row{display:grid;grid-template-columns:minmax(200px,1.2fr) minmax(220px,1.4fr) minmax(140px,.8fr) minmax(180px,1fr) auto;gap:12px;align-items:start}mat-form-field{width:100%}@media(max-width:960px){.surface-binding-row{grid-template-columns:minmax(0,1fr)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { 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: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.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.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i7.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
13635
+ }
13636
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SurfaceOpenActionEditorComponent, decorators: [{
13637
+ type: Component,
13638
+ args: [{ selector: 'praxis-surface-open-action-editor', standalone: true, providers: [providePraxisI18nConfig(SURFACE_OPEN_I18N_CONFIG)], imports: [
13639
+ CommonModule,
13640
+ FormsModule,
13641
+ MatButtonModule,
13642
+ MatFormFieldModule,
13643
+ MatIconModule,
13644
+ MatInputModule,
13645
+ MatSelectModule,
13646
+ MatSlideToggleModule,
13647
+ MatTooltipModule,
13648
+ ], template: `
13649
+ <div class="surface-editor">
13650
+ <div class="surface-section">
13651
+ <div class="surface-section-title">{{ t('editor.presets.title', 'Presets') }}</div>
13652
+ <div class="surface-preset-row">
13653
+ @for (preset of availablePresets; track preset.id) {
13654
+ <button
13655
+ mat-stroked-button
13656
+ type="button"
13657
+ (click)="applyPreset(preset)"
13658
+ [matTooltip]="presetDescription(preset)"
13659
+ >
13660
+ {{ presetLabel(preset) }}
13661
+ </button>
13662
+ }
13663
+ </div>
13664
+ <div class="surface-empty">
13665
+ {{
13666
+ t(
13667
+ 'editor.presets.help',
13668
+ 'Aplique um preset para começar com inputs mínimos e depois configure bindings conforme a origem da action.'
13669
+ )
13670
+ }}
13671
+ </div>
13672
+ </div>
13673
+
13674
+ <div class="surface-section">
13675
+ <div class="surface-section-title">{{ t('editor.surface.title', 'Surface') }}</div>
13676
+ <div class="surface-grid">
13677
+ <mat-form-field appearance="outline">
13678
+ <mat-label>{{ t('editor.surface.presentation', 'Apresentação') }}</mat-label>
13679
+ <mat-select
13680
+ [ngModel]="draft.presentation"
13681
+ (ngModelChange)="updatePresentation($event)"
13682
+ [ngModelOptions]="{ standalone: true }"
13683
+ >
13684
+ <mat-option value="modal">{{ t('editor.surface.presentation.modal', 'Modal') }}</mat-option>
13685
+ <mat-option value="drawer">{{ t('editor.surface.presentation.drawer', 'Drawer') }}</mat-option>
13686
+ </mat-select>
13687
+ </mat-form-field>
13688
+
13689
+ <mat-form-field appearance="outline">
13690
+ <mat-label>{{ t('editor.surface.titleLabel', 'Título') }}</mat-label>
13691
+ <input
13692
+ matInput
13693
+ [ngModel]="draft.title || ''"
13694
+ (ngModelChange)="updateTextField('title', $event)"
13695
+ [ngModelOptions]="{ standalone: true }"
13696
+ />
13697
+ </mat-form-field>
13698
+
13699
+ <mat-form-field appearance="outline">
13700
+ <mat-label>{{ t('editor.surface.subtitleLabel', 'Subtítulo') }}</mat-label>
13701
+ <input
13702
+ matInput
13703
+ [ngModel]="draft.subtitle || ''"
13704
+ (ngModelChange)="updateTextField('subtitle', $event)"
13705
+ [ngModelOptions]="{ standalone: true }"
13706
+ />
13707
+ </mat-form-field>
13708
+
13709
+ <mat-form-field appearance="outline">
13710
+ <mat-label>{{ t('editor.surface.iconLabel', 'Ícone') }}</mat-label>
13711
+ <input
13712
+ matInput
13713
+ [ngModel]="draft.icon || ''"
13714
+ (ngModelChange)="updateTextField('icon', $event)"
13715
+ [ngModelOptions]="{ standalone: true }"
13716
+ placeholder="open_in_new"
13717
+ />
13718
+ </mat-form-field>
13719
+
13720
+ <mat-form-field appearance="outline">
13721
+ <mat-label>{{ t('editor.surface.widthLabel', 'Largura') }}</mat-label>
13722
+ <input
13723
+ matInput
13724
+ [ngModel]="draft.size?.width || ''"
13725
+ (ngModelChange)="updateSizeField('width', $event)"
13726
+ [ngModelOptions]="{ standalone: true }"
13727
+ placeholder="720px"
13728
+ />
13729
+ </mat-form-field>
13730
+
13731
+ <mat-form-field appearance="outline">
13732
+ <mat-label>{{ t('editor.surface.heightLabel', 'Altura') }}</mat-label>
13733
+ <input
13734
+ matInput
13735
+ [ngModel]="draft.size?.height || ''"
13736
+ (ngModelChange)="updateSizeField('height', $event)"
13737
+ [ngModelOptions]="{ standalone: true }"
13738
+ placeholder="80vh"
13739
+ />
13740
+ </mat-form-field>
13741
+
13742
+ <mat-form-field appearance="outline">
13743
+ <mat-label>{{ t('editor.surface.minWidthLabel', 'Largura mínima') }}</mat-label>
13744
+ <input
13745
+ matInput
13746
+ [ngModel]="draft.size?.minWidth || ''"
13747
+ (ngModelChange)="updateSizeField('minWidth', $event)"
13748
+ [ngModelOptions]="{ standalone: true }"
13749
+ placeholder="360px"
13750
+ />
13751
+ </mat-form-field>
13752
+
13753
+ <mat-form-field appearance="outline">
13754
+ <mat-label>{{ t('editor.surface.maxWidthLabel', 'Largura máxima') }}</mat-label>
13755
+ <input
13756
+ matInput
13757
+ [ngModel]="draft.size?.maxWidth || ''"
13758
+ (ngModelChange)="updateSizeField('maxWidth', $event)"
13759
+ [ngModelOptions]="{ standalone: true }"
13760
+ placeholder="90vw"
13761
+ />
13762
+ </mat-form-field>
13763
+ </div>
13764
+ </div>
13765
+
13766
+ <div class="surface-section">
13767
+ <div class="surface-section-title">{{ t('editor.component.title', 'Componente') }}</div>
13768
+ <div class="surface-grid">
13769
+ <mat-form-field appearance="outline" class="surface-span-2">
13770
+ <mat-label>{{ t('editor.component.select', 'Componente registrado') }}</mat-label>
13771
+ <mat-select
13772
+ [ngModel]="draft.widget.id"
13773
+ (ngModelChange)="updateWidgetId($event)"
13774
+ [ngModelOptions]="{ standalone: true }"
13775
+ >
13776
+ <mat-option value="">{{ t('editor.component.selectPlaceholder', '-- Selecionar --') }}</mat-option>
13777
+ @for (option of componentOptions; track option.id) {
13778
+ <mat-option [value]="option.id">
13779
+ {{ option.friendlyName || option.id }}
13780
+ </mat-option>
13781
+ }
13782
+ </mat-select>
13783
+ </mat-form-field>
13784
+
13785
+ @if (selectedComponentMeta) {
13786
+ <div class="surface-component-meta surface-span-2">
13787
+ <div class="surface-component-title">
13788
+ {{ selectedComponentMeta.friendlyName || selectedComponentMeta.id }}
13789
+ </div>
13790
+ @if (selectedComponentMeta.description) {
13791
+ <div class="surface-component-description">
13792
+ {{ selectedComponentMeta.description }}
13793
+ </div>
13794
+ }
13795
+ </div>
13796
+ }
13797
+
13798
+ <mat-form-field appearance="outline" class="surface-span-2">
13799
+ <mat-label>{{ t('editor.component.bindingOrder', 'bindingOrder (CSV)') }}</mat-label>
13800
+ <input
13801
+ matInput
13802
+ [ngModel]="bindingOrderText"
13803
+ (ngModelChange)="updateBindingOrder($event)"
13804
+ [ngModelOptions]="{ standalone: true }"
13805
+ [placeholder]="t('editor.component.bindingOrderPlaceholder', 'resourcePath, formId, resourceId')"
13806
+ />
13807
+ </mat-form-field>
13808
+ </div>
13809
+ </div>
13810
+
13811
+ <div class="surface-section">
13812
+ <div class="surface-section-title">{{ t('editor.inputs.title', 'Inputs') }}</div>
13813
+ @if (selectedInputs.length) {
13814
+ <div class="surface-grid">
13815
+ @for (input of selectedInputs; track input.name) {
13816
+ @if (isBooleanType(input.type)) {
13817
+ <mat-slide-toggle
13818
+ [ngModel]="getBooleanInputValue(input.name)"
13819
+ (ngModelChange)="updateBooleanInput(input.name, $event)"
13820
+ [ngModelOptions]="{ standalone: true }"
13821
+ >
13822
+ {{ input.label || input.name }}
13823
+ </mat-slide-toggle>
13824
+ } @else if (isJsonType(input.type)) {
13825
+ <mat-form-field appearance="outline" class="surface-span-2">
13826
+ <mat-label>{{ input.label || input.name }}</mat-label>
13827
+ <textarea
13828
+ matInput
13829
+ rows="4"
13830
+ [ngModel]="getJsonInputDraft(input.name)"
13831
+ (ngModelChange)="updateJsonInput(input.name, $event)"
13832
+ [ngModelOptions]="{ standalone: true }"
13833
+ [placeholder]="jsonPlaceholder(input)"
13834
+ ></textarea>
13835
+ @if (input.description) {
13836
+ <mat-hint>{{ input.description }}</mat-hint>
13837
+ }
13838
+ @if (inputErrors[input.name]) {
13839
+ <mat-error>{{ inputErrors[input.name] }}</mat-error>
13840
+ }
13841
+ </mat-form-field>
13842
+ } @else {
13843
+ <mat-form-field appearance="outline">
13844
+ <mat-label>{{ input.label || input.name }}</mat-label>
13845
+ <input
13846
+ matInput
13847
+ [type]="isNumberType(input.type) ? 'number' : 'text'"
13848
+ [ngModel]="getScalarInputValue(input.name)"
13849
+ (ngModelChange)="updateScalarInput(input.name, input.type, $event)"
13850
+ [ngModelOptions]="{ standalone: true }"
13851
+ [placeholder]="scalarPlaceholder(input)"
13852
+ />
13853
+ @if (input.description) {
13854
+ <mat-hint>{{ input.description }}</mat-hint>
13855
+ }
13856
+ </mat-form-field>
13857
+ }
13858
+ }
13859
+ </div>
13860
+ } @else {
13861
+ <div class="surface-empty">
13862
+ {{ t('editor.inputs.empty', 'Selecione um componente registrado para configurar inputs públicos.') }}
13863
+ </div>
13864
+ }
13865
+ </div>
13866
+
13867
+ <div class="surface-section">
13868
+ <div class="surface-section-header">
13869
+ <div class="surface-section-title">{{ t('editor.bindings.title', 'Bindings') }}</div>
13870
+ <button mat-stroked-button type="button" (click)="addBinding()">
13871
+ <mat-icon>add</mat-icon>
13872
+ {{ t('editor.bindings.add', 'Binding') }}
13873
+ </button>
13874
+ </div>
13875
+ @if (draft.bindings?.length) {
13876
+ <div class="surface-bindings">
13877
+ @for (binding of draft.bindings || []; track $index) {
13878
+ <div class="surface-binding-row">
13879
+ <mat-form-field appearance="outline">
13880
+ <mat-label>{{ t('editor.bindings.source', 'Origem') }}</mat-label>
13881
+ <input
13882
+ matInput
13883
+ [ngModel]="binding.from || ''"
13884
+ (ngModelChange)="updateBinding($index, 'from', $event)"
13885
+ [ngModelOptions]="{ standalone: true }"
13886
+ [attr.list]="sourceSuggestionsListId"
13887
+ placeholder="payload.row.id"
13888
+ />
13889
+ </mat-form-field>
13890
+
13891
+ <mat-form-field appearance="outline">
13892
+ <mat-label>{{ t('editor.bindings.target', 'Destino') }}</mat-label>
13893
+ <input
13894
+ matInput
13895
+ [ngModel]="binding.to"
13896
+ (ngModelChange)="updateBinding($index, 'to', $event)"
13897
+ [ngModelOptions]="{ standalone: true }"
13898
+ [attr.list]="targetSuggestionsListId"
13899
+ placeholder="widget.inputs.resourceId"
13900
+ />
13901
+ </mat-form-field>
13902
+
13903
+ <mat-form-field appearance="outline">
13904
+ <mat-label>{{ t('editor.bindings.mode', 'Modo') }}</mat-label>
13905
+ <mat-select
13906
+ [ngModel]="binding.mode || 'path'"
13907
+ (ngModelChange)="updateBinding($index, 'mode', $event)"
13908
+ [ngModelOptions]="{ standalone: true }"
13909
+ >
13910
+ <mat-option value="path">path</mat-option>
13911
+ <mat-option value="template">template</mat-option>
13912
+ <mat-option value="constant">constant</mat-option>
13913
+ </mat-select>
13914
+ </mat-form-field>
13915
+
13916
+ @if ((binding.mode || 'path') === 'constant') {
13917
+ <mat-form-field appearance="outline">
13918
+ <mat-label>{{ t('editor.bindings.constantValue', 'Valor constante') }}</mat-label>
13919
+ <input
13920
+ matInput
13921
+ [ngModel]="binding.value ?? ''"
13922
+ (ngModelChange)="updateBinding($index, 'value', $event)"
13923
+ [ngModelOptions]="{ standalone: true }"
13924
+ />
13925
+ </mat-form-field>
13926
+ }
13927
+
13928
+ <button
13929
+ mat-icon-button
13930
+ color="warn"
13931
+ type="button"
13932
+ (click)="removeBinding($index)"
13933
+ [attr.aria-label]="t('editor.bindings.remove', 'Remover binding')"
13934
+ >
13935
+ <mat-icon>delete</mat-icon>
13936
+ </button>
13937
+ </div>
13938
+ }
13939
+ </div>
13940
+ } @else {
13941
+ <div class="surface-empty">
13942
+ {{ t('editor.bindings.empty', 'Nenhum binding configurado ainda.') }}
13943
+ </div>
13944
+ }
13945
+ <div class="surface-empty">
13946
+ @if (bindingSourceSuggestionsPreview) {
13947
+ <div>
13948
+ {{ t('editor.bindings.suggestedSources', 'Origens sugeridas: {{value}}', { value: bindingSourceSuggestionsPreview }) }}
13949
+ </div>
13950
+ }
13951
+ @if (bindingTargetSuggestionsPreview) {
13952
+ <div>
13953
+ {{ t('editor.bindings.suggestedTargets', 'Destinos sugeridos: {{value}}', { value: bindingTargetSuggestionsPreview }) }}
13954
+ </div>
13955
+ }
13956
+ </div>
13957
+ <datalist [id]="sourceSuggestionsListId">
13958
+ @for (suggestion of bindingSourceSuggestions; track suggestion) {
13959
+ <option [value]="suggestion"></option>
13960
+ }
13961
+ </datalist>
13962
+ <datalist [id]="targetSuggestionsListId">
13963
+ @for (suggestion of bindingTargetSuggestions; track suggestion) {
13964
+ <option [value]="suggestion"></option>
13965
+ }
13966
+ </datalist>
13967
+ </div>
13968
+
13969
+ <div class="surface-section">
13970
+ <div class="surface-section-title">{{ t('editor.context.title', 'Contexto extra') }}</div>
13971
+ <mat-form-field appearance="outline" class="surface-span-all">
13972
+ <mat-label>{{ t('editor.context.label', 'context (JSON)') }}</mat-label>
13973
+ <textarea
13974
+ matInput
13975
+ rows="4"
13976
+ [ngModel]="contextDraft"
13977
+ (ngModelChange)="updateContext($event)"
13978
+ [ngModelOptions]="{ standalone: true }"
13979
+ placeholder="{ }"
13980
+ ></textarea>
13981
+ <mat-hint>{{ t('editor.context.hint', 'Opcional. Mesclado ao contexto da action no runtime.') }}</mat-hint>
13982
+ @if (contextError) {
13983
+ <mat-error>{{ contextError }}</mat-error>
13984
+ }
13985
+ </mat-form-field>
13986
+ </div>
13987
+ </div>
13988
+ `, styles: [".surface-editor{display:grid;gap:16px}.surface-section{padding:12px 14px;border:1px dashed var(--md-sys-color-outline-variant, rgba(0, 0, 0, .18));border-radius:10px;background:var(--md-sys-color-surface-container-lowest, #fff)}.surface-section-header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}.surface-section-title{font-size:12px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant, #5f6368);margin-bottom:10px}.surface-section-header .surface-section-title{margin-bottom:0}.surface-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px 16px}.surface-preset-row{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}.surface-span-2{grid-column:span 2}.surface-span-all{width:100%}.surface-component-meta{display:grid;gap:4px}.surface-component-title{font-weight:600}.surface-component-description,.surface-empty{color:var(--md-sys-color-on-surface-variant, #5f6368);font-size:12px}.surface-bindings{display:grid;gap:12px}.surface-binding-row{display:grid;grid-template-columns:minmax(200px,1.2fr) minmax(220px,1.4fr) minmax(140px,.8fr) minmax(180px,1fr) auto;gap:12px;align-items:start}mat-form-field{width:100%}@media(max-width:960px){.surface-binding-row{grid-template-columns:minmax(0,1fr)}}\n"] }]
13989
+ }], propDecorators: { value: [{
13990
+ type: Input
13991
+ }], hostKind: [{
13992
+ type: Input
13993
+ }], valueChange: [{
13994
+ type: Output
13995
+ }] } });
13996
+
13997
+ /**
13998
+ * Catálogo de capacidades genéricas de FieldMetadata para uso da IA.
13999
+ * Baseado em projects/praxis-core/src/lib/models/component-metadata.interface.ts
14000
+ */
14001
+ const ENUMS$1 = {
14002
+ fieldControlType: Object.values(FieldControlType),
14003
+ fieldDataType: Object.values(FieldDataType),
14004
+ componentContext: ['form', 'filter', 'table', 'dialog', 'standalone'],
14005
+ iconPosition: ['start', 'end'],
14006
+ iconSize: ['small', 'medium', 'large'],
14007
+ validationMode: ['immediate', 'blur', 'submit'],
14008
+ validatorTrigger: ['change', 'blur', 'submit', 'immediate'],
14009
+ validatorErrorPosition: ['bottom', 'top', 'tooltip'],
14010
+ dependencyMergeStrategy: ['replace', 'merge'],
14011
+ dependencyLoadOnChange: ['respectLoadOn', 'immediate', 'manual'],
14012
+ textTransformApply: ['displayOnly', 'saveOnly', 'both'],
14013
+ visibleIn: ['form', 'filter', 'table', 'dialog'],
14014
+ appearance: ['fill', 'outline'],
14015
+ color: ['primary', 'accent', 'warn'],
14016
+ floatLabel: ['auto', 'always', 'never'],
14017
+ hintPosition: ['start', 'end'],
14018
+ align: ['start', 'center', 'end', 'stretch'],
14019
+ sectionTitleStyle: ['titleLarge', 'titleMedium', 'titleSmall', 'headlineSmall'],
14020
+ sectionDescriptionStyle: ['bodyLarge', 'bodyMedium', 'bodySmall'],
14021
+ };
14022
+ const FIELD_METADATA_CAPABILITIES = {
14023
+ version: 'v1.3',
14024
+ enums: ENUMS$1,
14025
+ notes: [
14026
+ 'Este catálogo define as capacidades genéricas de FieldMetadata.',
14027
+ 'O host típico para estes metadados é FormConfig (via fieldMetadata[]) ou FormLayout.',
14028
+ 'Detalhes específicos de cada controle (ex: opções de datepicker, máscaras específicas) devem ser consultados nos catálogos de microcomponentes.',
14029
+ 'POLICY: Arrays e objetos (ex: options, validators) devem sofrer merge/append, nunca substituição completa sem confirmação.',
14030
+ 'Funções (conditionalRequired, transforms) não são serializáveis. O LLM não deve gerar código de função, apenas expressões string se suportado pelo interpretador, ou solicitar configuração manual.',
14031
+ ],
12089
14032
  capabilities: [
12090
14033
  // =============================================================================
12091
14034
  // COMPONENT METADATA (BASE)
@@ -13547,7 +15490,7 @@ class PraxisFooterLinksComponent {
13547
15490
  </a>
13548
15491
  </nav>
13549
15492
  </footer>
13550
- `, isInline: true, styles: [":host{display:block;--pfl-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 78%, transparent);--pfl-brand-color: var(--md-sys-color-on-surface);--pfl-secondary-color: var(--md-sys-color-on-surface-variant);--pfl-link-color: var(--md-sys-color-primary);--pfl-surface-bg: color-mix(in srgb, var(--md-sys-color-surface-container-low) 92%, transparent)}:host-context(.mdc-theme-dark),:host-context(.theme-dark){--pfl-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 90%, transparent);--pfl-link-color: color-mix(in srgb, var(--md-sys-color-primary) 86%, white);--pfl-surface-bg: color-mix(in srgb, var(--md-sys-color-surface-container-low) 96%, transparent)}.pfl-shell{display:flex;align-items:center;justify-content:space-between;gap:16px 24px;padding:16px 4px 4px;border-top:1px solid var(--pfl-border)}.pfl-stacked{flex-direction:column;align-items:flex-start}.pfl-plain{padding-top:6px;border-top:0}.pfl-surface{padding:16px 18px;border-top:0;border-radius:18px;background:var(--pfl-surface-bg)}.pfl-brand{display:grid;gap:4px}.pfl-brand-text{color:var(--pfl-brand-color);font-size:.92rem;font-weight:700}.pfl-secondary-text{color:var(--pfl-secondary-color);font-size:.8rem;line-height:1.5}.pfl-nav{display:flex;flex-wrap:wrap;gap:10px 16px}.pfl-link{color:var(--pfl-link-color);font-size:.84rem;line-height:1.5;text-decoration:underline;text-underline-offset:2px}@media(max-width:720px){.pfl-shell{flex-direction:column;align-items:flex-start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
15493
+ `, isInline: true, styles: [":host{display:block;--pfl-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 78%, transparent);--pfl-brand-color: var(--md-sys-color-on-surface);--pfl-secondary-color: var(--md-sys-color-on-surface-variant);--pfl-link-color: var(--md-sys-color-primary);--pfl-surface-bg: color-mix(in srgb, var(--md-sys-color-surface-container-low) 92%, transparent)}:host-context(.mdc-theme-dark),:host-context(.theme-dark){--pfl-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 90%, transparent);--pfl-link-color: color-mix(in srgb, var(--md-sys-color-primary) 86%, white);--pfl-surface-bg: color-mix(in srgb, var(--md-sys-color-surface-container-low) 96%, transparent)}.pfl-shell{display:flex;align-items:center;justify-content:space-between;gap:16px 24px;padding:16px 4px 4px;border-top:1px solid var(--pfl-border)}.pfl-stacked{flex-direction:column;align-items:flex-start}.pfl-plain{padding-top:6px;border-top:0}.pfl-surface{padding:16px 18px;border-top:0;border-radius:18px;background:var(--pfl-surface-bg)}.pfl-brand{display:grid;gap:4px}.pfl-brand-text{color:var(--pfl-brand-color);font-size:.92rem;font-weight:700}.pfl-secondary-text{color:var(--pfl-secondary-color);font-size:.8rem;line-height:1.5}.pfl-nav{display:flex;flex-wrap:wrap;gap:10px 16px}.pfl-link{color:var(--pfl-link-color);font-size:.84rem;line-height:1.5;text-decoration:underline;text-underline-offset:2px}@media(max-width:720px){.pfl-shell{flex-direction:column;align-items:flex-start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13551
15494
  }
13552
15495
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisFooterLinksComponent, decorators: [{
13553
15496
  type: Component,
@@ -13699,7 +15642,7 @@ class PraxisHeroBannerComponent {
13699
15642
  </div>
13700
15643
  </ng-template>
13701
15644
  </section>
13702
- `, isInline: true, styles: [":host{display:block;--phb-shell-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 60%, transparent);--phb-shell-shadow: 0 24px 60px color-mix(in srgb, var(--md-sys-color-shadow, #000) 10%, transparent);--phb-shell-bg: radial-gradient(circle at top right, color-mix(in srgb, var(--md-sys-color-primary) 14%, transparent), transparent 38%), linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-surface-container-lowest) 92%, var(--md-sys-color-primary-container) 8%) 0%, color-mix(in srgb, var(--md-sys-color-surface-container-low) 88%, var(--md-sys-color-primary-container) 12%) 54%, color-mix(in srgb, var(--md-sys-color-surface) 96%, var(--md-sys-color-surface-container) 4%) 100% );--phb-event-bg: radial-gradient(circle at top right, rgba(255, 184, 77, .24), transparent 34%), linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-primary-container) 16%, var(--md-sys-color-surface-container-lowest) 84%) 0%, color-mix(in srgb, var(--md-sys-color-primary-container) 24%, var(--md-sys-color-surface-container-low) 76%) 45%, color-mix(in srgb, var(--md-sys-color-surface) 92%, var(--md-sys-color-primary-container) 8%) 100% );--phb-institutional-bg: radial-gradient(circle at top right, rgba(17, 94, 89, .2), transparent 34%), linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-secondary-container) 18%, var(--md-sys-color-surface-container-lowest) 82%) 0%, color-mix(in srgb, var(--md-sys-color-secondary-container) 24%, var(--md-sys-color-surface-container-low) 76%) 48%, color-mix(in srgb, var(--md-sys-color-surface) 94%, var(--md-sys-color-secondary-container) 6%) 100% );--phb-brand-color: color-mix(in srgb, var(--md-sys-color-primary) 68%, var(--md-sys-color-on-surface) 32%);--phb-subtitle-color: color-mix(in srgb, var(--md-sys-color-primary) 52%, var(--md-sys-color-on-surface) 48%);--phb-title-color: var(--md-sys-color-on-surface);--phb-description-color: var(--md-sys-color-on-surface-variant);--phb-badge-bg: color-mix(in srgb, var(--md-sys-color-surface-container-high) 78%, transparent);--phb-badge-color: var(--md-sys-color-on-surface);--phb-badge-highlight-bg: color-mix(in srgb, var(--md-sys-color-primary-container) 78%, transparent);--phb-badge-highlight-color: var(--md-sys-color-on-primary-container);--phb-badge-muted-bg: color-mix(in srgb, var(--md-sys-color-surface-container-highest) 82%, transparent);--phb-badge-muted-color: var(--md-sys-color-on-surface-variant);--phb-badge-warning-bg: color-mix(in srgb, var(--md-sys-color-error-container) 74%, transparent);--phb-badge-warning-color: var(--md-sys-color-on-error-container);--phb-meta-bg: color-mix(in srgb, var(--md-sys-color-surface-container-lowest) 72%, transparent);--phb-meta-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 48%, transparent);--phb-meta-label-color: var(--md-sys-color-on-surface-variant);--phb-meta-value-color: var(--md-sys-color-on-surface);--phb-visual-bg: linear-gradient( 160deg, color-mix(in srgb, var(--md-sys-color-surface-container-high) 90%, var(--md-sys-color-primary) 10%), color-mix(in srgb, var(--md-sys-color-surface-container-low) 88%, var(--md-sys-color-primary-container) 12%) );--phb-visual-fallback: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-surface-bright, var(--md-sys-color-surface-container-high)) 24%, transparent), color-mix(in srgb, var(--md-sys-color-surface-container-lowest) 8%, transparent) ), linear-gradient( 145deg, color-mix(in srgb, var(--md-sys-color-primary) 18%, var(--md-sys-color-surface-container-high) 82%), color-mix(in srgb, var(--md-sys-color-primary-container) 18%, var(--md-sys-color-surface-container-low) 82%) );--phb-orb-a-bg: color-mix(in srgb, var(--md-sys-color-surface-bright, var(--md-sys-color-surface-container-high)) 68%, transparent);--phb-orb-b-bg: color-mix(in srgb, var(--md-sys-color-primary) 22%, transparent);--phb-grid-line: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent)}:host-context(.mdc-theme-dark),:host-context(.theme-dark){--phb-shell-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 84%, transparent);--phb-shell-shadow: 0 22px 54px rgba(0, 0, 0, .28);--phb-shell-bg: radial-gradient(circle at top right, color-mix(in srgb, var(--md-sys-color-primary) 16%, transparent), transparent 40%), linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-surface-container-lowest) 90%, var(--md-sys-color-primary-container) 10%) 0%, color-mix(in srgb, var(--md-sys-color-surface-container-low) 84%, var(--md-sys-color-primary-container) 16%) 54%, color-mix(in srgb, var(--md-sys-color-surface) 92%, var(--md-sys-color-primary-container) 8%) 100% );--phb-event-bg: radial-gradient(circle at top right, rgba(255, 184, 77, .18), transparent 36%), linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-primary-container) 16%, var(--md-sys-color-surface-container-lowest) 84%) 0%, color-mix(in srgb, var(--md-sys-color-primary-container) 22%, var(--md-sys-color-surface-container-low) 78%) 45%, color-mix(in srgb, var(--md-sys-color-surface-container) 90%, var(--md-sys-color-primary-container) 10%) 100% );--phb-institutional-bg: radial-gradient(circle at top right, rgba(17, 94, 89, .16), transparent 36%), linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-secondary-container) 16%, var(--md-sys-color-surface-container-lowest) 84%) 0%, color-mix(in srgb, var(--md-sys-color-secondary-container) 20%, var(--md-sys-color-surface-container-low) 80%) 48%, color-mix(in srgb, var(--md-sys-color-surface-container) 92%, var(--md-sys-color-secondary-container) 8%) 100% );--phb-meta-bg: color-mix(in srgb, var(--md-sys-color-surface-container-lowest) 70%, transparent);--phb-meta-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 60%, transparent);--phb-visual-bg: linear-gradient( 160deg, color-mix(in srgb, var(--md-sys-color-surface-container-high) 88%, var(--md-sys-color-primary) 12%), color-mix(in srgb, var(--md-sys-color-surface-container) 82%, var(--md-sys-color-primary-container) 18%) );--phb-visual-fallback: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-surface-container-high) 22%, transparent), color-mix(in srgb, var(--md-sys-color-surface-container-lowest) 8%, transparent) ), linear-gradient( 145deg, color-mix(in srgb, var(--md-sys-color-primary) 22%, var(--md-sys-color-surface-container-high) 78%), color-mix(in srgb, var(--md-sys-color-primary-container) 24%, var(--md-sys-color-surface-container-low) 76%) );--phb-orb-a-bg: color-mix(in srgb, var(--md-sys-color-surface-container-highest) 34%, transparent);--phb-orb-b-bg: color-mix(in srgb, var(--md-sys-color-primary) 26%, transparent);--phb-grid-line: color-mix(in srgb, var(--md-sys-color-on-surface) 10%, transparent)}.phb-shell{display:grid;grid-template-columns:minmax(0,1.45fr) minmax(240px,.95fr);gap:22px;align-items:stretch;padding:22px;border-radius:28px;overflow:hidden;background:var(--phb-shell-bg);border:1px solid var(--phb-shell-border);box-shadow:var(--phb-shell-shadow)}.phb-event{background:var(--phb-event-bg)}.phb-institutional{background:var(--phb-institutional-bg)}.phb-copy{display:grid;gap:18px;min-width:0}.phb-brand{margin:0;color:var(--phb-brand-color);font-size:.88rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.phb-badges{display:flex;flex-wrap:wrap;gap:8px}.phb-badge{display:inline-flex;align-items:center;min-height:28px;padding:0 12px;border-radius:999px;background:var(--phb-badge-bg);color:var(--phb-badge-color);font-size:.76rem;font-weight:700;letter-spacing:.02em}.phb-badge-highlight{background:var(--phb-badge-highlight-bg);color:var(--phb-badge-highlight-color)}.phb-badge-muted{background:var(--phb-badge-muted-bg);color:var(--phb-badge-muted-color)}.phb-badge-warning{background:var(--phb-badge-warning-bg);color:var(--phb-badge-warning-color)}.phb-text{display:grid;gap:8px}.phb-subtitle{margin:0;color:var(--phb-subtitle-color);font-size:.9rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em}.phb-title{margin:0;color:var(--phb-title-color);font-size:clamp(1.6rem,2.2vw,2.5rem);line-height:1.08;letter-spacing:-.03em}.phb-title-accent{display:inline;margin-left:.18em;background:linear-gradient(90deg,#4285f4,#6ea8ff 28%,#a142f4 62%,#ea4335);-webkit-background-clip:text;background-clip:text;color:transparent}.phb-description{margin:0;max-width:56ch;color:var(--phb-description-color);font-size:.98rem;line-height:1.65}.phb-meta{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin:0}.phb-meta-item{display:grid;gap:4px;padding:12px 14px;border-radius:16px;background:var(--phb-meta-bg);border:1px solid var(--phb-meta-border);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}.phb-meta-item dt{margin:0;color:var(--phb-meta-label-color);font-size:.73rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em}.phb-meta-item dd{margin:0;color:var(--phb-meta-value-color);font-size:.92rem;line-height:1.45}.phb-visual{position:relative;min-height:220px;border-radius:22px;overflow:hidden;background:var(--phb-visual-bg)}.phb-image{display:block;width:100%;height:100%;object-fit:cover}.phb-visual-fallback{background:var(--phb-visual-fallback)}.phb-flat{padding:0 0 18px;border:0;border-radius:0;box-shadow:none;background:transparent;grid-template-columns:minmax(0,1.15fr) minmax(220px,.85fr);gap:18px}.phb-flat .phb-brand{color:var(--md-sys-color-primary)}.phb-flat .phb-badges{order:3}.phb-flat .phb-meta{grid-template-columns:1fr;gap:6px}.phb-flat .phb-meta-item{padding:0;border:0;border-radius:0;background:transparent;-webkit-backdrop-filter:none;backdrop-filter:none}.phb-flat .phb-meta-item dt,.phb-flat .phb-meta-item dd{display:inline}.phb-flat .phb-meta-item dt{margin-right:6px}.phb-flat .phb-visual{min-height:190px;border-radius:26px}.phb-orb{position:absolute;border-radius:999px;filter:blur(2px)}.phb-orb-a{inset:24px auto auto 20px;width:86px;height:86px;background:var(--phb-orb-a-bg)}.phb-orb-b{inset:auto 16px 18px auto;width:132px;height:132px;background:var(--phb-orb-b-bg)}.phb-grid{position:absolute;inset:0;background-image:linear-gradient(var(--phb-grid-line) 1px,transparent 1px),linear-gradient(90deg,var(--phb-grid-line) 1px,transparent 1px);background-size:24px 24px;opacity:.5}@media(max-width:900px){.phb-shell{grid-template-columns:1fr;padding:18px}.phb-visual{order:-1;min-height:180px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
15645
+ `, isInline: true, styles: [":host{display:block;--phb-shell-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 60%, transparent);--phb-shell-shadow: 0 24px 60px color-mix(in srgb, var(--md-sys-color-shadow, #000) 10%, transparent);--phb-shell-bg: radial-gradient(circle at top right, color-mix(in srgb, var(--md-sys-color-primary) 14%, transparent), transparent 38%), linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-surface-container-lowest) 92%, var(--md-sys-color-primary-container) 8%) 0%, color-mix(in srgb, var(--md-sys-color-surface-container-low) 88%, var(--md-sys-color-primary-container) 12%) 54%, color-mix(in srgb, var(--md-sys-color-surface) 96%, var(--md-sys-color-surface-container) 4%) 100% );--phb-event-bg: radial-gradient(circle at top right, rgba(255, 184, 77, .24), transparent 34%), linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-primary-container) 16%, var(--md-sys-color-surface-container-lowest) 84%) 0%, color-mix(in srgb, var(--md-sys-color-primary-container) 24%, var(--md-sys-color-surface-container-low) 76%) 45%, color-mix(in srgb, var(--md-sys-color-surface) 92%, var(--md-sys-color-primary-container) 8%) 100% );--phb-institutional-bg: radial-gradient(circle at top right, rgba(17, 94, 89, .2), transparent 34%), linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-secondary-container) 18%, var(--md-sys-color-surface-container-lowest) 82%) 0%, color-mix(in srgb, var(--md-sys-color-secondary-container) 24%, var(--md-sys-color-surface-container-low) 76%) 48%, color-mix(in srgb, var(--md-sys-color-surface) 94%, var(--md-sys-color-secondary-container) 6%) 100% );--phb-brand-color: color-mix(in srgb, var(--md-sys-color-primary) 68%, var(--md-sys-color-on-surface) 32%);--phb-subtitle-color: color-mix(in srgb, var(--md-sys-color-primary) 52%, var(--md-sys-color-on-surface) 48%);--phb-title-color: var(--md-sys-color-on-surface);--phb-description-color: var(--md-sys-color-on-surface-variant);--phb-badge-bg: color-mix(in srgb, var(--md-sys-color-surface-container-high) 78%, transparent);--phb-badge-color: var(--md-sys-color-on-surface);--phb-badge-highlight-bg: color-mix(in srgb, var(--md-sys-color-primary-container) 78%, transparent);--phb-badge-highlight-color: var(--md-sys-color-on-primary-container);--phb-badge-muted-bg: color-mix(in srgb, var(--md-sys-color-surface-container-highest) 82%, transparent);--phb-badge-muted-color: var(--md-sys-color-on-surface-variant);--phb-badge-warning-bg: color-mix(in srgb, var(--md-sys-color-error-container) 74%, transparent);--phb-badge-warning-color: var(--md-sys-color-on-error-container);--phb-meta-bg: color-mix(in srgb, var(--md-sys-color-surface-container-lowest) 72%, transparent);--phb-meta-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 48%, transparent);--phb-meta-label-color: var(--md-sys-color-on-surface-variant);--phb-meta-value-color: var(--md-sys-color-on-surface);--phb-visual-bg: linear-gradient( 160deg, color-mix(in srgb, var(--md-sys-color-surface-container-high) 90%, var(--md-sys-color-primary) 10%), color-mix(in srgb, var(--md-sys-color-surface-container-low) 88%, var(--md-sys-color-primary-container) 12%) );--phb-visual-fallback: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-surface-bright, var(--md-sys-color-surface-container-high)) 24%, transparent), color-mix(in srgb, var(--md-sys-color-surface-container-lowest) 8%, transparent) ), linear-gradient( 145deg, color-mix(in srgb, var(--md-sys-color-primary) 18%, var(--md-sys-color-surface-container-high) 82%), color-mix(in srgb, var(--md-sys-color-primary-container) 18%, var(--md-sys-color-surface-container-low) 82%) );--phb-orb-a-bg: color-mix(in srgb, var(--md-sys-color-surface-bright, var(--md-sys-color-surface-container-high)) 68%, transparent);--phb-orb-b-bg: color-mix(in srgb, var(--md-sys-color-primary) 22%, transparent);--phb-grid-line: color-mix(in srgb, var(--md-sys-color-on-surface) 12%, transparent)}:host-context(.mdc-theme-dark),:host-context(.theme-dark){--phb-shell-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 84%, transparent);--phb-shell-shadow: 0 22px 54px rgba(0, 0, 0, .28);--phb-shell-bg: radial-gradient(circle at top right, color-mix(in srgb, var(--md-sys-color-primary) 16%, transparent), transparent 40%), linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-surface-container-lowest) 90%, var(--md-sys-color-primary-container) 10%) 0%, color-mix(in srgb, var(--md-sys-color-surface-container-low) 84%, var(--md-sys-color-primary-container) 16%) 54%, color-mix(in srgb, var(--md-sys-color-surface) 92%, var(--md-sys-color-primary-container) 8%) 100% );--phb-event-bg: radial-gradient(circle at top right, rgba(255, 184, 77, .18), transparent 36%), linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-primary-container) 16%, var(--md-sys-color-surface-container-lowest) 84%) 0%, color-mix(in srgb, var(--md-sys-color-primary-container) 22%, var(--md-sys-color-surface-container-low) 78%) 45%, color-mix(in srgb, var(--md-sys-color-surface-container) 90%, var(--md-sys-color-primary-container) 10%) 100% );--phb-institutional-bg: radial-gradient(circle at top right, rgba(17, 94, 89, .16), transparent 36%), linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-secondary-container) 16%, var(--md-sys-color-surface-container-lowest) 84%) 0%, color-mix(in srgb, var(--md-sys-color-secondary-container) 20%, var(--md-sys-color-surface-container-low) 80%) 48%, color-mix(in srgb, var(--md-sys-color-surface-container) 92%, var(--md-sys-color-secondary-container) 8%) 100% );--phb-meta-bg: color-mix(in srgb, var(--md-sys-color-surface-container-lowest) 70%, transparent);--phb-meta-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 60%, transparent);--phb-visual-bg: linear-gradient( 160deg, color-mix(in srgb, var(--md-sys-color-surface-container-high) 88%, var(--md-sys-color-primary) 12%), color-mix(in srgb, var(--md-sys-color-surface-container) 82%, var(--md-sys-color-primary-container) 18%) );--phb-visual-fallback: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-surface-container-high) 22%, transparent), color-mix(in srgb, var(--md-sys-color-surface-container-lowest) 8%, transparent) ), linear-gradient( 145deg, color-mix(in srgb, var(--md-sys-color-primary) 22%, var(--md-sys-color-surface-container-high) 78%), color-mix(in srgb, var(--md-sys-color-primary-container) 24%, var(--md-sys-color-surface-container-low) 76%) );--phb-orb-a-bg: color-mix(in srgb, var(--md-sys-color-surface-container-highest) 34%, transparent);--phb-orb-b-bg: color-mix(in srgb, var(--md-sys-color-primary) 26%, transparent);--phb-grid-line: color-mix(in srgb, var(--md-sys-color-on-surface) 10%, transparent)}.phb-shell{display:grid;grid-template-columns:minmax(0,1.45fr) minmax(240px,.95fr);gap:22px;align-items:stretch;padding:22px;border-radius:28px;overflow:hidden;background:var(--phb-shell-bg);border:1px solid var(--phb-shell-border);box-shadow:var(--phb-shell-shadow)}.phb-event{background:var(--phb-event-bg)}.phb-institutional{background:var(--phb-institutional-bg)}.phb-copy{display:grid;gap:18px;min-width:0}.phb-brand{margin:0;color:var(--phb-brand-color);font-size:.88rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.phb-badges{display:flex;flex-wrap:wrap;gap:8px}.phb-badge{display:inline-flex;align-items:center;min-height:28px;padding:0 12px;border-radius:999px;background:var(--phb-badge-bg);color:var(--phb-badge-color);font-size:.76rem;font-weight:700;letter-spacing:.02em}.phb-badge-highlight{background:var(--phb-badge-highlight-bg);color:var(--phb-badge-highlight-color)}.phb-badge-muted{background:var(--phb-badge-muted-bg);color:var(--phb-badge-muted-color)}.phb-badge-warning{background:var(--phb-badge-warning-bg);color:var(--phb-badge-warning-color)}.phb-text{display:grid;gap:8px}.phb-subtitle{margin:0;color:var(--phb-subtitle-color);font-size:.9rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em}.phb-title{margin:0;color:var(--phb-title-color);font-size:clamp(1.6rem,2.2vw,2.5rem);line-height:1.08;letter-spacing:-.03em}.phb-title-accent{display:inline;margin-left:.18em;background:linear-gradient(90deg,#4285f4,#6ea8ff 28%,#a142f4 62%,#ea4335);-webkit-background-clip:text;background-clip:text;color:transparent}.phb-description{margin:0;max-width:56ch;color:var(--phb-description-color);font-size:.98rem;line-height:1.65}.phb-meta{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin:0}.phb-meta-item{display:grid;gap:4px;padding:12px 14px;border-radius:16px;background:var(--phb-meta-bg);border:1px solid var(--phb-meta-border);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}.phb-meta-item dt{margin:0;color:var(--phb-meta-label-color);font-size:.73rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em}.phb-meta-item dd{margin:0;color:var(--phb-meta-value-color);font-size:.92rem;line-height:1.45}.phb-visual{position:relative;min-height:220px;border-radius:22px;overflow:hidden;background:var(--phb-visual-bg)}.phb-image{display:block;width:100%;height:100%;object-fit:cover}.phb-visual-fallback{background:var(--phb-visual-fallback)}.phb-flat{padding:0 0 18px;border:0;border-radius:0;box-shadow:none;background:transparent;grid-template-columns:minmax(0,1.15fr) minmax(220px,.85fr);gap:18px}.phb-flat .phb-brand{color:var(--md-sys-color-primary)}.phb-flat .phb-badges{order:3}.phb-flat .phb-meta{grid-template-columns:1fr;gap:6px}.phb-flat .phb-meta-item{padding:0;border:0;border-radius:0;background:transparent;-webkit-backdrop-filter:none;backdrop-filter:none}.phb-flat .phb-meta-item dt,.phb-flat .phb-meta-item dd{display:inline}.phb-flat .phb-meta-item dt{margin-right:6px}.phb-flat .phb-visual{min-height:190px;border-radius:26px}.phb-orb{position:absolute;border-radius:999px;filter:blur(2px)}.phb-orb-a{inset:24px auto auto 20px;width:86px;height:86px;background:var(--phb-orb-a-bg)}.phb-orb-b{inset:auto 16px 18px auto;width:132px;height:132px;background:var(--phb-orb-b-bg)}.phb-grid{position:absolute;inset:0;background-image:linear-gradient(var(--phb-grid-line) 1px,transparent 1px),linear-gradient(90deg,var(--phb-grid-line) 1px,transparent 1px);background-size:24px 24px;opacity:.5}@media(max-width:900px){.phb-shell{grid-template-columns:1fr;padding:18px}.phb-visual{order:-1;min-height:180px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13703
15646
  }
13704
15647
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisHeroBannerComponent, decorators: [{
13705
15648
  type: Component,
@@ -13954,7 +15897,7 @@ class PraxisRichTextBlockComponent {
13954
15897
 
13955
15898
  <div class="prt-content" [innerHTML]="renderedContent"></div>
13956
15899
  </section>
13957
- `, isInline: true, styles: [":host{display:block;--prt-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 72%, transparent);--prt-bg: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-surface) 96%, var(--md-sys-color-surface-container-lowest) 4%), color-mix(in srgb, var(--md-sys-color-surface-container-low) 92%, var(--md-sys-color-surface) 8%) );--prt-emphasis-border: color-mix(in srgb, var(--md-sys-color-primary) 32%, var(--md-sys-color-outline-variant));--prt-emphasis-bg: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-primary-container) 36%, var(--md-sys-color-surface) 64%), color-mix(in srgb, var(--md-sys-color-surface) 96%, var(--md-sys-color-primary-container) 4%) );--prt-subtle-bg: color-mix(in srgb, var(--md-sys-color-surface-container-low) 82%, transparent);--prt-icon-bg: color-mix(in srgb, var(--md-sys-color-primary-container) 64%, transparent);--prt-code-bg: color-mix(in srgb, var(--md-sys-color-surface-container-highest) 82%, transparent)}:host-context(.mdc-theme-dark),:host-context(.theme-dark){--prt-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 84%, transparent);--prt-bg: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-surface-container-low) 92%, var(--md-sys-color-surface) 8%), color-mix(in srgb, var(--md-sys-color-surface-container) 90%, var(--md-sys-color-surface-container-high) 10%) );--prt-emphasis-border: color-mix(in srgb, var(--md-sys-color-primary) 42%, var(--md-sys-color-outline-variant));--prt-emphasis-bg: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-primary-container) 28%, var(--md-sys-color-surface-container-low) 72%), color-mix(in srgb, var(--md-sys-color-surface-container) 92%, var(--md-sys-color-primary-container) 8%) );--prt-subtle-bg: color-mix(in srgb, var(--md-sys-color-surface-container-low) 88%, transparent);--prt-icon-bg: color-mix(in srgb, var(--md-sys-color-primary-container) 42%, transparent);--prt-code-bg: color-mix(in srgb, var(--md-sys-color-surface-container-high) 88%, transparent)}.prt-block{display:grid;gap:12px;padding:16px 18px;border-radius:16px;border:1px solid var(--prt-border);background:var(--prt-bg);color:var(--md-sys-color-on-surface)}.prt-block-emphasis{border-color:var(--prt-emphasis-border);background:var(--prt-emphasis-bg)}.prt-block-subtle{background:var(--prt-subtle-bg);border-style:dashed}.prt-block-plain{padding:0;border:0;border-radius:0;background:transparent}.prt-head{display:grid;grid-template-columns:auto 1fr;gap:12px;align-items:start}.prt-icon{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:12px;background:var(--prt-icon-bg);color:var(--md-sys-color-primary);font-size:20px;line-height:1}.prt-title-wrap{display:grid;gap:4px}.prt-title{margin:0;font-size:1rem;font-weight:700;line-height:1.3}.prt-subtitle{margin:0;color:var(--md-sys-color-on-surface-variant);font-size:.86rem;line-height:1.4}.prt-content{color:var(--md-sys-color-on-surface);font-size:.94rem;line-height:1.6}.prt-content :where(p,ul){margin:0}.prt-content :where(p+p,p+ul,ul+p,ul+ul){margin-top:10px}.prt-content ul{padding-left:18px}.prt-content a{color:var(--md-sys-color-primary);text-decoration:underline;text-underline-offset:2px}.prt-content strong{font-weight:700}.prt-content em{font-style:italic}.prt-content code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.88em;padding:.1em .35em;border-radius:6px;background:var(--prt-code-bg)}.prt-block-plain .prt-icon{width:32px;height:32px;border-radius:10px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 80%,transparent)}.prt-block-plain .prt-content{font-size:.92rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
15900
+ `, isInline: true, styles: [":host{display:block;--prt-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 72%, transparent);--prt-bg: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-surface) 96%, var(--md-sys-color-surface-container-lowest) 4%), color-mix(in srgb, var(--md-sys-color-surface-container-low) 92%, var(--md-sys-color-surface) 8%) );--prt-emphasis-border: color-mix(in srgb, var(--md-sys-color-primary) 32%, var(--md-sys-color-outline-variant));--prt-emphasis-bg: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-primary-container) 36%, var(--md-sys-color-surface) 64%), color-mix(in srgb, var(--md-sys-color-surface) 96%, var(--md-sys-color-primary-container) 4%) );--prt-subtle-bg: color-mix(in srgb, var(--md-sys-color-surface-container-low) 82%, transparent);--prt-icon-bg: color-mix(in srgb, var(--md-sys-color-primary-container) 64%, transparent);--prt-code-bg: color-mix(in srgb, var(--md-sys-color-surface-container-highest) 82%, transparent)}:host-context(.mdc-theme-dark),:host-context(.theme-dark){--prt-border: color-mix(in srgb, var(--md-sys-color-outline-variant) 84%, transparent);--prt-bg: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-surface-container-low) 92%, var(--md-sys-color-surface) 8%), color-mix(in srgb, var(--md-sys-color-surface-container) 90%, var(--md-sys-color-surface-container-high) 10%) );--prt-emphasis-border: color-mix(in srgb, var(--md-sys-color-primary) 42%, var(--md-sys-color-outline-variant));--prt-emphasis-bg: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-primary-container) 28%, var(--md-sys-color-surface-container-low) 72%), color-mix(in srgb, var(--md-sys-color-surface-container) 92%, var(--md-sys-color-primary-container) 8%) );--prt-subtle-bg: color-mix(in srgb, var(--md-sys-color-surface-container-low) 88%, transparent);--prt-icon-bg: color-mix(in srgb, var(--md-sys-color-primary-container) 42%, transparent);--prt-code-bg: color-mix(in srgb, var(--md-sys-color-surface-container-high) 88%, transparent)}.prt-block{display:grid;gap:12px;padding:16px 18px;border-radius:16px;border:1px solid var(--prt-border);background:var(--prt-bg);color:var(--md-sys-color-on-surface)}.prt-block-emphasis{border-color:var(--prt-emphasis-border);background:var(--prt-emphasis-bg)}.prt-block-subtle{background:var(--prt-subtle-bg);border-style:dashed}.prt-block-plain{padding:0;border:0;border-radius:0;background:transparent}.prt-head{display:grid;grid-template-columns:auto 1fr;gap:12px;align-items:start}.prt-icon{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:12px;background:var(--prt-icon-bg);color:var(--md-sys-color-primary);font-size:20px;line-height:1}.prt-title-wrap{display:grid;gap:4px}.prt-title{margin:0;font-size:1rem;font-weight:700;line-height:1.3}.prt-subtitle{margin:0;color:var(--md-sys-color-on-surface-variant);font-size:.86rem;line-height:1.4}.prt-content{color:var(--md-sys-color-on-surface);font-size:.94rem;line-height:1.6}.prt-content :where(p,ul){margin:0}.prt-content :where(p+p,p+ul,ul+p,ul+ul){margin-top:10px}.prt-content ul{padding-left:18px}.prt-content a{color:var(--md-sys-color-primary);text-decoration:underline;text-underline-offset:2px}.prt-content strong{font-weight:700}.prt-content em{font-style:italic}.prt-content code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.88em;padding:.1em .35em;border-radius:6px;background:var(--prt-code-bg)}.prt-block-plain .prt-icon{width:32px;height:32px;border-radius:10px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 80%,transparent)}.prt-block-plain .prt-content{font-size:.92rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13958
15901
  }
13959
15902
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisRichTextBlockComponent, decorators: [{
13960
15903
  type: Component,
@@ -14160,7 +16103,7 @@ class PraxisLegalNoticeComponent {
14160
16103
  </a>
14161
16104
  </nav>
14162
16105
  </section>
14163
- `, isInline: true, styles: [":host{display:block}.pln-shell{display:grid;gap:10px}.pln-shell .praxis-rich-text-block .prt-block,.pln-shell .prt-block{border-left:4px solid color-mix(in srgb,var(--md-sys-color-primary) 56%,transparent)}.pln-warning .praxis-rich-text-block .prt-block,.pln-warning .prt-block{border-left-color:var(--md-sys-color-error)}.pln-muted .praxis-rich-text-block .prt-block,.pln-muted .prt-block{border-left-color:color-mix(in srgb,var(--md-sys-color-outline) 58%,transparent)}.pln-plain .praxis-rich-text-block .prt-block,.pln-plain .prt-block{border-left:0}.pln-links{display:flex;flex-wrap:wrap;gap:8px 12px;padding-left:18px}.pln-plain .pln-links{padding-left:0;padding-top:2px}.pln-link{color:var(--md-sys-color-primary);font-size:.84rem;line-height:1.4;text-decoration:underline;text-underline-offset:2px}:host-context(.mdc-theme-dark) .pln-link,:host-context(.theme-dark) .pln-link{color:color-mix(in srgb,var(--md-sys-color-primary) 86%,white)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: PraxisRichTextBlockComponent, selector: "praxis-rich-text-block", inputs: ["instanceId", "analyticsId", "ariaLabel", "title", "subtitle", "icon", "variant", "appearance", "contentFormat", "content"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
16106
+ `, isInline: true, styles: [":host{display:block}.pln-shell{display:grid;gap:10px}.pln-shell .praxis-rich-text-block .prt-block,.pln-shell .prt-block{border-left:4px solid color-mix(in srgb,var(--md-sys-color-primary) 56%,transparent)}.pln-warning .praxis-rich-text-block .prt-block,.pln-warning .prt-block{border-left-color:var(--md-sys-color-error)}.pln-muted .praxis-rich-text-block .prt-block,.pln-muted .prt-block{border-left-color:color-mix(in srgb,var(--md-sys-color-outline) 58%,transparent)}.pln-plain .praxis-rich-text-block .prt-block,.pln-plain .prt-block{border-left:0}.pln-links{display:flex;flex-wrap:wrap;gap:8px 12px;padding-left:18px}.pln-plain .pln-links{padding-left:0;padding-top:2px}.pln-link{color:var(--md-sys-color-primary);font-size:.84rem;line-height:1.4;text-decoration:underline;text-underline-offset:2px}:host-context(.mdc-theme-dark) .pln-link,:host-context(.theme-dark) .pln-link{color:color-mix(in srgb,var(--md-sys-color-primary) 86%,white)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: PraxisRichTextBlockComponent, selector: "praxis-rich-text-block", inputs: ["instanceId", "analyticsId", "ariaLabel", "title", "subtitle", "icon", "variant", "appearance", "contentFormat", "content"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14164
16107
  }
14165
16108
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisLegalNoticeComponent, decorators: [{
14166
16109
  type: Component,
@@ -14350,7 +16293,7 @@ class PraxisUserContextSummaryComponent {
14350
16293
  <button type="button" class="pux-action" (click)="emitAction()">{{ actionLabel }}</button>
14351
16294
  </div>
14352
16295
  </section>
14353
- `, isInline: true, styles: [":host{display:block}.pux-card{display:grid;gap:14px;padding:16px 18px;border-radius:18px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 78%,transparent);background:linear-gradient(180deg,#fffffffa,#f4f8fcfa)}.pux-head{display:grid;gap:4px}.pux-title{margin:0;color:var(--md-sys-color-on-surface);font-size:.98rem;font-weight:700}.pux-subtitle{margin:0;color:var(--md-sys-color-on-surface-variant);font-size:.84rem;line-height:1.5}.pux-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin:0}.pux-item{display:grid;gap:4px;padding:12px 14px;border-radius:14px;background:#ffffffb8;border:1px solid rgba(66,93,135,.1)}.pux-item dt{margin:0;color:#5d7186;font-size:.73rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em}.pux-item dd{margin:0;color:#17314f;font-size:.92rem;line-height:1.45}.pux-actions{display:flex;justify-content:flex-start}.pux-action{min-height:36px;padding:0 14px;border:1px solid rgba(33,88,201,.18);border-radius:999px;background:#2060d214;color:#1e55b8;font:inherit;font-size:.84rem;font-weight:700;cursor:pointer}.pux-empty{margin:0;color:var(--md-sys-color-on-surface-variant);font-size:.84rem}.pux-plain{gap:10px;padding:8px 0 4px;border:0;border-radius:0;background:transparent}.pux-plain .pux-grid{grid-template-columns:1fr;gap:6px}.pux-plain .pux-item{padding:12px 14px;border-radius:14px;background:#ffffff8f;border:1px solid rgba(66,93,135,.08)}.pux-plain .pux-actions{margin-top:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
16296
+ `, isInline: true, styles: [":host{display:block}.pux-card{display:grid;gap:14px;padding:16px 18px;border-radius:18px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 78%,transparent);background:linear-gradient(180deg,#fffffffa,#f4f8fcfa)}.pux-head{display:grid;gap:4px}.pux-title{margin:0;color:var(--md-sys-color-on-surface);font-size:.98rem;font-weight:700}.pux-subtitle{margin:0;color:var(--md-sys-color-on-surface-variant);font-size:.84rem;line-height:1.5}.pux-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin:0}.pux-item{display:grid;gap:4px;padding:12px 14px;border-radius:14px;background:#ffffffb8;border:1px solid rgba(66,93,135,.1)}.pux-item dt{margin:0;color:#5d7186;font-size:.73rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em}.pux-item dd{margin:0;color:#17314f;font-size:.92rem;line-height:1.45}.pux-actions{display:flex;justify-content:flex-start}.pux-action{min-height:36px;padding:0 14px;border:1px solid rgba(33,88,201,.18);border-radius:999px;background:#2060d214;color:#1e55b8;font:inherit;font-size:.84rem;font-weight:700;cursor:pointer}.pux-empty{margin:0;color:var(--md-sys-color-on-surface-variant);font-size:.84rem}.pux-plain{gap:10px;padding:8px 0 4px;border:0;border-radius:0;background:transparent}.pux-plain .pux-grid{grid-template-columns:1fr;gap:6px}.pux-plain .pux-item{padding:12px 14px;border-radius:14px;background:#ffffff8f;border:1px solid rgba(66,93,135,.08)}.pux-plain .pux-actions{margin-top:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14354
16297
  }
14355
16298
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisUserContextSummaryComponent, decorators: [{
14356
16299
  type: Component,
@@ -15102,7 +17045,7 @@ class WidgetShellComponent {
15102
17045
  @if (expanded || fullscreen) {
15103
17046
  <div class="pdx-shell-backdrop" (click)="closeOverlay()"></div>
15104
17047
  }
15105
- `, isInline: true, styles: [":host{display:block;height:100%}.pdx-shell{position:relative;height:100%;display:flex;flex-direction:column}.pdx-shell.no-shell{background:transparent;border:none;border-radius:0;box-shadow:none}.pdx-shell.dashboard{background:var(--pdx-shell-card-bg, var(--pdx-dashboard-card-bg, var(--md-sys-color-surface-container-low)));border:1px solid var(--pdx-shell-card-border, var(--pdx-dashboard-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}.pdx-shell-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))}.pdx-shell-title{display:flex;align-items:center;gap:10px;min-width:0;flex:1;color:var(--pdx-shell-title-color, inherit)}.pdx-shell-title mat-icon{color:var(--pdx-shell-icon-color, currentColor)}.pdx-shell-text{min-width:0}.pdx-shell-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}.pdx-shell-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}.pdx-shell-actions,.pdx-shell-window-actions{display:flex;align-items:center;gap:6px}.pdx-shell-window-actions{margin-left:auto}.pdx-action-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-action-text{padding:0 8px}.pdx-action-label{font-size:12px;font-weight:500}.pdx-shell-body{flex:1;min-height:0;padding:var(--pdx-shell-body-padding, 10px 12px 12px 12px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit)}.pdx-shell.no-shell .pdx-shell-body{padding:0}.pdx-shell-body.hidden{display:none}.pdx-shell.collapsed .pdx-shell-header{border-bottom-color:transparent}.pdx-shell.expanded .pdx-shell-body,.pdx-shell.fullscreen .pdx-shell-body{overflow:auto;display:flex;flex-direction:column;min-height:0}.pdx-shell.expanded .pdx-shell-body>*,.pdx-shell.fullscreen .pdx-shell-body>*{flex:1 1 auto;min-height:0;width:100%}.pdx-shell.expanded{position:fixed;top:10vh;left:50%;width:min(920px,92vw);height:min(640px,82vh);transform:translate(-50%);z-index:1200;box-shadow:var(--mat-elevation-level8)}.pdx-shell.fullscreen{position:fixed;top:50%;left:50%;width:95vw;height:95vh;transform:translate(-50%,-50%);z-index:1201;box-shadow:var(--mat-elevation-level8)}.pdx-shell-backdrop{position:fixed;inset:0;z-index:1199;background:#0000008c;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i4$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i4$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
17048
+ `, isInline: true, styles: [":host{display:block;height:100%}.pdx-shell{position:relative;height:100%;display:flex;flex-direction:column}.pdx-shell.no-shell{background:transparent;border:none;border-radius:0;box-shadow:none}.pdx-shell.dashboard{background:var(--pdx-shell-card-bg, var(--pdx-dashboard-card-bg, var(--md-sys-color-surface-container-low)));border:1px solid var(--pdx-shell-card-border, var(--pdx-dashboard-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}.pdx-shell-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))}.pdx-shell-title{display:flex;align-items:center;gap:10px;min-width:0;flex:1;color:var(--pdx-shell-title-color, inherit)}.pdx-shell-title mat-icon{color:var(--pdx-shell-icon-color, currentColor)}.pdx-shell-text{min-width:0}.pdx-shell-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}.pdx-shell-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}.pdx-shell-actions,.pdx-shell-window-actions{display:flex;align-items:center;gap:6px}.pdx-shell-window-actions{margin-left:auto}.pdx-action-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-action-text{padding:0 8px}.pdx-action-label{font-size:12px;font-weight:500}.pdx-shell-body{flex:1;min-height:0;padding:var(--pdx-shell-body-padding, 10px 12px 12px 12px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit)}.pdx-shell.no-shell .pdx-shell-body{padding:0}.pdx-shell-body.hidden{display:none}.pdx-shell.collapsed .pdx-shell-header{border-bottom-color:transparent}.pdx-shell.expanded .pdx-shell-body,.pdx-shell.fullscreen .pdx-shell-body{overflow:auto;display:flex;flex-direction:column;min-height:0}.pdx-shell.expanded .pdx-shell-body>*,.pdx-shell.fullscreen .pdx-shell-body>*{flex:1 1 auto;min-height:0;width:100%}.pdx-shell.expanded{position:fixed;top:10vh;left:50%;width:min(920px,92vw);height:min(640px,82vh);transform:translate(-50%);z-index:1200;box-shadow:var(--mat-elevation-level8)}.pdx-shell.fullscreen{position:fixed;top:50%;left:50%;width:95vw;height:95vh;transform:translate(-50%,-50%);z-index:1201;box-shadow:var(--mat-elevation-level8)}.pdx-shell-backdrop{position:fixed;inset:0;z-index:1199;background:#0000008c;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i4$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i4$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
15106
17049
  }
15107
17050
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: WidgetShellComponent, decorators: [{
15108
17051
  type: Component,
@@ -15243,6 +17186,180 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
15243
17186
  args: [DynamicWidgetLoaderDirective]
15244
17187
  }] } });
15245
17188
 
17189
+ const BUILTIN_PAGE_LAYOUT_PRESETS = {
17190
+ 'analytics-overview': {
17191
+ id: 'analytics-overview',
17192
+ label: 'Analytics Overview',
17193
+ description: 'Hero, KPIs, charts principais e tabela detalhada em uma página analítica ampla.',
17194
+ category: 'analytics',
17195
+ defaultLayout: {
17196
+ orientation: 'columns',
17197
+ columns: 2,
17198
+ gap: '20px',
17199
+ breakpoints: { sm: 1, md: 2, lg: 2, xl: 2 },
17200
+ },
17201
+ defaultGrouping: [
17202
+ { kind: 'hero', id: 'hero', widgetKeys: ['hero'], emphasis: 'high' },
17203
+ { kind: 'section', id: 'kpis', label: 'KPIs', widgetKeys: ['kpis'], layout: 'row' },
17204
+ { kind: 'section', id: 'charts', label: 'Charts', widgetKeys: ['primary-chart', 'secondary-chart-a', 'secondary-chart-b'], layout: 'grid' },
17205
+ { kind: 'section', id: 'detail-table', label: 'Detail Table', widgetKeys: ['detail-table'], layout: 'stack' },
17206
+ ],
17207
+ defaultThemePreset: 'analytics-calm',
17208
+ slotModel: [
17209
+ { id: 'hero', label: 'Hero', required: false, maxItems: 1 },
17210
+ { id: 'kpis', label: 'KPIs', required: true, minItems: 1 },
17211
+ { id: 'primary-chart', label: 'Primary Chart', required: true, minItems: 1, maxItems: 1 },
17212
+ { id: 'secondary-chart-a', label: 'Secondary Chart A', required: false, maxItems: 1 },
17213
+ { id: 'secondary-chart-b', label: 'Secondary Chart B', required: false, maxItems: 1 },
17214
+ { id: 'detail-table', label: 'Detail Table', required: true, minItems: 1, maxItems: 1 },
17215
+ ],
17216
+ widgetSuggestions: [
17217
+ { slot: 'kpis', widgetType: 'form:user-context-summary', priority: 'primary' },
17218
+ { slot: 'primary-chart', widgetType: 'praxis-chart', priority: 'primary' },
17219
+ { slot: 'detail-table', widgetType: 'praxis-table', priority: 'primary' },
17220
+ ],
17221
+ devicePolicy: {
17222
+ stacking: 'compress',
17223
+ preferTabsOnMobile: false,
17224
+ },
17225
+ },
17226
+ 'kpi-plus-table': {
17227
+ id: 'kpi-plus-table',
17228
+ label: 'KPI Plus Table',
17229
+ description: 'Faixa superior de métricas com uma tabela dominante e visuais auxiliares.',
17230
+ category: 'analytics',
17231
+ defaultLayout: {
17232
+ orientation: 'columns',
17233
+ columns: 2,
17234
+ gap: '18px',
17235
+ breakpoints: { sm: 1, md: 1, lg: 2, xl: 2 },
17236
+ },
17237
+ defaultGrouping: [
17238
+ { kind: 'section', id: 'filters', label: 'Filters', widgetKeys: ['filters'], layout: 'stack' },
17239
+ { kind: 'section', id: 'kpis', label: 'KPIs', widgetKeys: ['kpis'], layout: 'row' },
17240
+ { kind: 'section', id: 'table', label: 'Table', widgetKeys: ['detail-table'], layout: 'stack' },
17241
+ { kind: 'section', id: 'aux', label: 'Auxiliary Charts', widgetKeys: ['aux-chart-a', 'aux-chart-b'], layout: 'grid' },
17242
+ ],
17243
+ defaultThemePreset: 'analytics-calm',
17244
+ slotModel: [
17245
+ { id: 'filters', label: 'Filters', required: false, maxItems: 1 },
17246
+ { id: 'kpis', label: 'KPIs', required: true, minItems: 1 },
17247
+ { id: 'detail-table', label: 'Detail Table', required: true, minItems: 1, maxItems: 1 },
17248
+ { id: 'aux-chart-a', label: 'Auxiliary Chart A', required: false, maxItems: 1 },
17249
+ { id: 'aux-chart-b', label: 'Auxiliary Chart B', required: false, maxItems: 1 },
17250
+ ],
17251
+ widgetSuggestions: [
17252
+ { slot: 'filters', widgetType: 'praxis-filter', priority: 'primary' },
17253
+ { slot: 'detail-table', widgetType: 'praxis-table', priority: 'primary' },
17254
+ ],
17255
+ devicePolicy: {
17256
+ stacking: 'linearize',
17257
+ hideSecondaryContentOnMobile: true,
17258
+ },
17259
+ },
17260
+ 'master-detail-dashboard': {
17261
+ id: 'master-detail-dashboard',
17262
+ label: 'Master Detail Dashboard',
17263
+ description: 'Master dominante com áreas de detalhe, KPIs e tabs sincronizadas.',
17264
+ category: 'master-detail',
17265
+ defaultLayout: {
17266
+ orientation: 'columns',
17267
+ columns: 3,
17268
+ gap: '18px',
17269
+ breakpoints: { sm: 1, md: 1, lg: 2, xl: 3 },
17270
+ },
17271
+ defaultGrouping: [
17272
+ { kind: 'section', id: 'master', label: 'Master', widgetKeys: ['master'], layout: 'stack' },
17273
+ { kind: 'section', id: 'detail-kpis', label: 'Detail KPIs', widgetKeys: ['detail-kpis'], layout: 'row' },
17274
+ {
17275
+ kind: 'tabs',
17276
+ id: 'detail-tabs',
17277
+ label: 'Detail Tabs',
17278
+ tabs: [
17279
+ { id: 'detail-insights', label: 'Insights', widgetKeys: ['detail-chart-a', 'detail-chart-b'] },
17280
+ { id: 'detail-table', label: 'Details', widgetKeys: ['detail-table'] },
17281
+ ],
17282
+ },
17283
+ ],
17284
+ defaultThemePreset: 'workspace-balanced',
17285
+ slotModel: [
17286
+ { id: 'master', label: 'Master', required: true, minItems: 1, maxItems: 1 },
17287
+ { id: 'detail-kpis', label: 'Detail KPIs', required: false, minItems: 1 },
17288
+ { id: 'detail-chart-a', label: 'Detail Chart A', required: false, maxItems: 1 },
17289
+ { id: 'detail-chart-b', label: 'Detail Chart B', required: false, maxItems: 1 },
17290
+ { id: 'detail-table', label: 'Detail Table', required: true, minItems: 1, maxItems: 1 },
17291
+ ],
17292
+ widgetSuggestions: [
17293
+ { slot: 'master', widgetType: 'praxis-table', priority: 'primary' },
17294
+ { slot: 'detail-table', widgetType: 'praxis-table', priority: 'primary' },
17295
+ { slot: 'detail-chart-a', widgetType: 'praxis-chart', priority: 'secondary' },
17296
+ ],
17297
+ devicePolicy: {
17298
+ stacking: 'compress',
17299
+ preferTabsOnMobile: true,
17300
+ },
17301
+ },
17302
+ 'ops-monitoring': {
17303
+ id: 'ops-monitoring',
17304
+ label: 'Ops Monitoring',
17305
+ description: 'Cards de status, séries temporais, fila operacional e área de alertas.',
17306
+ category: 'operations',
17307
+ defaultLayout: {
17308
+ orientation: 'columns',
17309
+ columns: 3,
17310
+ gap: '18px',
17311
+ breakpoints: { sm: 1, md: 2, lg: 3, xl: 3 },
17312
+ },
17313
+ defaultGrouping: [
17314
+ { kind: 'hero', id: 'status-cards', widgetKeys: ['status-cards'], emphasis: 'medium' },
17315
+ { kind: 'section', id: 'timelines', label: 'Timelines', widgetKeys: ['timeline-a', 'timeline-b'], layout: 'grid' },
17316
+ { kind: 'section', id: 'queue', label: 'Operational Queue', widgetKeys: ['queue'], layout: 'stack' },
17317
+ { kind: 'rail', id: 'alerts', side: 'right', widgetKeys: ['alerts'] },
17318
+ ],
17319
+ defaultThemePreset: 'ops-monitoring',
17320
+ slotModel: [
17321
+ { id: 'status-cards', label: 'Status Cards', required: true, minItems: 1 },
17322
+ { id: 'timeline-a', label: 'Timeline A', required: true, minItems: 1, maxItems: 1 },
17323
+ { id: 'timeline-b', label: 'Timeline B', required: false, maxItems: 1 },
17324
+ { id: 'queue', label: 'Operational Queue', required: true, minItems: 1, maxItems: 1 },
17325
+ { id: 'alerts', label: 'Alerts Rail', required: false, maxItems: 1 },
17326
+ ],
17327
+ widgetSuggestions: [
17328
+ { slot: 'status-cards', widgetType: 'form:user-context-summary', priority: 'primary' },
17329
+ { slot: 'timeline-a', widgetType: 'praxis-chart', priority: 'primary' },
17330
+ { slot: 'queue', widgetType: 'praxis-table', priority: 'primary' },
17331
+ ],
17332
+ devicePolicy: {
17333
+ stacking: 'compress',
17334
+ hideSecondaryContentOnMobile: true,
17335
+ preferTabsOnMobile: true,
17336
+ },
17337
+ },
17338
+ };
17339
+ const BUILTIN_PAGE_THEME_PRESETS = {
17340
+ 'analytics-calm': {
17341
+ id: 'analytics-calm',
17342
+ label: 'Analytics Calm',
17343
+ description: 'Superfícies leves, ênfase clara em dados e motion sutil.',
17344
+ density: 'comfortable',
17345
+ motion: 'subtle',
17346
+ },
17347
+ 'workspace-balanced': {
17348
+ id: 'workspace-balanced',
17349
+ label: 'Workspace Balanced',
17350
+ description: 'Equilíbrio entre densidade operacional e clareza visual.',
17351
+ density: 'comfortable',
17352
+ motion: 'subtle',
17353
+ },
17354
+ 'ops-monitoring': {
17355
+ id: 'ops-monitoring',
17356
+ label: 'Ops Monitoring',
17357
+ description: 'Contraste um pouco maior para leitura rápida de status, filas e alertas.',
17358
+ density: 'compact',
17359
+ motion: 'subtle',
17360
+ },
17361
+ };
17362
+
15246
17363
  class ConnectionManagerService {
15247
17364
  /** Extract value from an object using dot-path (e.g., 'payload.row.id'). */
15248
17365
  extractByPath(obj, path) {
@@ -15577,6 +17694,69 @@ class WidgetPageStateRuntimeService {
15577
17694
  }
15578
17695
  return omitted;
15579
17696
  }
17697
+ case 'aggregate-array': {
17698
+ const source = this.resolveNamedSource(options['source'], sources, env.state);
17699
+ const metrics = Array.isArray(options['metrics']) ? options['metrics'] : [];
17700
+ if (!Array.isArray(source))
17701
+ return {};
17702
+ return this.aggregateArray(source, metrics);
17703
+ }
17704
+ case 'build-field-list': {
17705
+ const source = this.resolveNamedSource(options['source'], sources, env.state);
17706
+ const items = Array.isArray(options['items']) ? options['items'] : [];
17707
+ return this.buildFieldList(source, items, env);
17708
+ }
17709
+ case 'group-top': {
17710
+ const source = this.resolveNamedSource(options['source'], sources, env.state);
17711
+ if (!Array.isArray(source))
17712
+ return [];
17713
+ return this.groupTop(source, options);
17714
+ }
17715
+ case 'when-present': {
17716
+ const source = this.resolveNamedSource(options['source'], sources, env.state);
17717
+ if (source == null)
17718
+ return options['fallback'];
17719
+ const text = String(source).trim();
17720
+ if (!text)
17721
+ return options['fallback'];
17722
+ return `${String(options['prefix'] || '')}${text}${String(options['suffix'] || '')}`;
17723
+ }
17724
+ case 'join-nonempty': {
17725
+ const values = Array.isArray(options['paths'])
17726
+ ? options['paths'].map((path) => this.readPath(env.state, String(path)))
17727
+ : sources;
17728
+ const parts = values
17729
+ .map((value) => value == null ? '' : String(value).trim())
17730
+ .filter((value) => value.length > 0);
17731
+ if (!parts.length)
17732
+ return options['fallback'];
17733
+ return parts.join(typeof options['delimiter'] === 'string' ? options['delimiter'] : ', ');
17734
+ }
17735
+ case 'format-value': {
17736
+ const source = this.resolveNamedSource(options['source'], sources, env.state);
17737
+ return this.formatFieldValue(source, options['format']);
17738
+ }
17739
+ case 'coalesce': {
17740
+ const values = Array.isArray(options['paths'])
17741
+ ? options['paths'].map((path) => this.readPath(env.state, String(path)))
17742
+ : sources;
17743
+ for (const value of values) {
17744
+ if (value == null)
17745
+ continue;
17746
+ if (typeof value === 'string' && !value.trim())
17747
+ continue;
17748
+ return value;
17749
+ }
17750
+ return options['fallback'];
17751
+ }
17752
+ case 'count-nonempty-entries': {
17753
+ const source = this.resolveNamedSource(options['source'], sources, env.state);
17754
+ if (!this.isPlainObject(source))
17755
+ return 0;
17756
+ return Object.values(source).filter((value) => !this.isEmptyValue(value)).length;
17757
+ }
17758
+ case 'select-case':
17759
+ return this.selectCase(options, env);
15580
17760
  case 'template':
15581
17761
  return this.resolveTemplate(options['value'], env);
15582
17762
  default:
@@ -15593,6 +17773,145 @@ class WidgetPageStateRuntimeService {
15593
17773
  resolveSourceValues(paths, state) {
15594
17774
  return (paths || []).map((path) => this.readPath(state, path));
15595
17775
  }
17776
+ aggregateArray(rows, metrics) {
17777
+ const output = {};
17778
+ for (const metric of metrics) {
17779
+ const key = typeof metric?.['key'] === 'string' ? metric['key'].trim() : '';
17780
+ const op = typeof metric?.['op'] === 'string' ? metric['op'].trim() : '';
17781
+ if (!key || !op)
17782
+ continue;
17783
+ switch (op) {
17784
+ case 'count':
17785
+ output[key] = rows.length;
17786
+ break;
17787
+ case 'sum':
17788
+ output[key] = rows.reduce((sum, row) => sum + this.toNumber(this.readMetricValue(row, metric['field'])), 0);
17789
+ break;
17790
+ case 'avg':
17791
+ output[key] = rows.length
17792
+ ? rows.reduce((sum, row) => sum + this.toNumber(this.readMetricValue(row, metric['field'])), 0) / rows.length
17793
+ : 0;
17794
+ break;
17795
+ case 'distinct-count': {
17796
+ const values = rows
17797
+ .map((row) => this.readMetricValue(row, metric['field']))
17798
+ .filter((value) => value !== null && value !== undefined && String(value).trim() !== '');
17799
+ output[key] = new Set(values.map((value) => String(value))).size;
17800
+ break;
17801
+ }
17802
+ }
17803
+ }
17804
+ return output;
17805
+ }
17806
+ buildFieldList(source, items, env) {
17807
+ const templateEnv = {
17808
+ ...env,
17809
+ source: this.clone(source),
17810
+ };
17811
+ return items.reduce((acc, item) => {
17812
+ if (!item || typeof item !== 'object')
17813
+ return acc;
17814
+ const label = typeof item['labelTemplate'] === 'string'
17815
+ ? this.resolveTemplate(item['labelTemplate'], templateEnv)
17816
+ : this.resolveTemplate(item['label'] ?? '', templateEnv);
17817
+ const rawValue = typeof item['valueTemplate'] === 'string'
17818
+ ? this.resolveTemplate(item['valueTemplate'], templateEnv)
17819
+ : this.readPath(source || {}, String(item['valuePath'] || '').trim());
17820
+ const formattedValue = this.formatFieldValue(rawValue, item['format']);
17821
+ acc.push({
17822
+ label: typeof label === 'string' ? label : String(label ?? ''),
17823
+ value: formattedValue,
17824
+ });
17825
+ return acc;
17826
+ }, []);
17827
+ }
17828
+ groupTop(rows, options) {
17829
+ const groupBy = typeof options['groupBy'] === 'string' ? options['groupBy'].trim() : '';
17830
+ if (!groupBy)
17831
+ return [];
17832
+ const aggregate = typeof options['aggregate'] === 'string' ? options['aggregate'].trim() : 'count';
17833
+ const field = typeof options['field'] === 'string' ? options['field'].trim() : '';
17834
+ const limit = typeof options['limit'] === 'number' && options['limit'] > 0 ? Math.floor(options['limit']) : 5;
17835
+ const totals = new Map();
17836
+ for (const row of rows) {
17837
+ const groupValue = this.readMetricValue(row, groupBy);
17838
+ const key = String(groupValue ?? '').trim();
17839
+ if (!key)
17840
+ continue;
17841
+ const amount = aggregate === 'sum'
17842
+ ? this.toNumber(this.readMetricValue(row, field))
17843
+ : 1;
17844
+ totals.set(key, (totals.get(key) || 0) + amount);
17845
+ }
17846
+ return Array.from(totals.entries())
17847
+ .sort((left, right) => right[1] - left[1])
17848
+ .slice(0, limit)
17849
+ .map(([value, total]) => ({
17850
+ label: value,
17851
+ value,
17852
+ metric: this.formatFieldValue(total, options['metricFormat'] || 'integer'),
17853
+ }));
17854
+ }
17855
+ selectCase(options, env) {
17856
+ const cases = Array.isArray(options['cases']) ? options['cases'] : [];
17857
+ for (const item of cases) {
17858
+ if (!item || typeof item !== 'object')
17859
+ continue;
17860
+ if (!this.matchesCase(item['when'], env.state))
17861
+ continue;
17862
+ return this.resolveCaseValue(item, env);
17863
+ }
17864
+ return this.resolveCaseValue({
17865
+ value: options['defaultValue'],
17866
+ template: options['defaultTemplate'],
17867
+ path: options['defaultPath'],
17868
+ }, env);
17869
+ }
17870
+ matchesCase(condition, state) {
17871
+ if (!condition || typeof condition !== 'object')
17872
+ return false;
17873
+ const path = typeof condition['path'] === 'string' ? condition['path'].trim() : '';
17874
+ const value = path ? this.readPath(state, path) : undefined;
17875
+ const compareValue = condition['comparePath']
17876
+ ? this.readPath(state, String(condition['comparePath']).trim())
17877
+ : condition['value'];
17878
+ if ('equals' in condition)
17879
+ return value === condition['equals'];
17880
+ if ('notEquals' in condition)
17881
+ return value !== condition['notEquals'];
17882
+ if (condition['truthy'] === true)
17883
+ return Boolean(value);
17884
+ if (condition['falsy'] === true)
17885
+ return !value;
17886
+ if (condition['notEmpty'] === true)
17887
+ return value != null && String(value).trim().length > 0;
17888
+ if ('lt' in condition)
17889
+ return this.toNumber(value) < this.toNumber(condition['lt']);
17890
+ if ('lte' in condition)
17891
+ return this.toNumber(value) <= this.toNumber(condition['lte']);
17892
+ if ('gt' in condition)
17893
+ return this.toNumber(value) > this.toNumber(condition['gt']);
17894
+ if ('gte' in condition)
17895
+ return this.toNumber(value) >= this.toNumber(condition['gte']);
17896
+ if ('comparePath' in condition && 'op' in condition) {
17897
+ switch (String(condition['op']).trim()) {
17898
+ case 'lt': return this.toNumber(value) < this.toNumber(compareValue);
17899
+ case 'lte': return this.toNumber(value) <= this.toNumber(compareValue);
17900
+ case 'gt': return this.toNumber(value) > this.toNumber(compareValue);
17901
+ case 'gte': return this.toNumber(value) >= this.toNumber(compareValue);
17902
+ case 'equals': return value === compareValue;
17903
+ }
17904
+ }
17905
+ return false;
17906
+ }
17907
+ resolveCaseValue(item, env) {
17908
+ if ('path' in item && typeof item['path'] === 'string' && item['path'].trim()) {
17909
+ return this.readPath(env.state, item['path'].trim());
17910
+ }
17911
+ if ('template' in item)
17912
+ return this.resolveTemplate(item['template'], env);
17913
+ return item['value'];
17914
+ }
15596
17915
  resolveTemplate(node, env) {
15597
17916
  if (node == null)
15598
17917
  return node;
@@ -15626,6 +17945,65 @@ class WidgetPageStateRuntimeService {
15626
17945
  isEqual(left, right) {
15627
17946
  return JSON.stringify(left) === JSON.stringify(right);
15628
17947
  }
17948
+ isEmptyValue(value) {
17949
+ if (value == null)
17950
+ return true;
17951
+ if (typeof value === 'string')
17952
+ return value.trim().length === 0;
17953
+ if (Array.isArray(value))
17954
+ return value.length === 0 || value.every((item) => this.isEmptyValue(item));
17955
+ if (this.isPlainObject(value))
17956
+ return Object.values(value).every((item) => this.isEmptyValue(item));
17957
+ return false;
17958
+ }
17959
+ readMetricValue(row, field) {
17960
+ if (typeof field !== 'string' || !field.trim())
17961
+ return row;
17962
+ return this.readPath(row || {}, field.trim());
17963
+ }
17964
+ toNumber(value) {
17965
+ if (typeof value === 'number')
17966
+ return Number.isFinite(value) ? value : 0;
17967
+ if (typeof value === 'string' && value.trim()) {
17968
+ const parsed = Number(value);
17969
+ return Number.isFinite(parsed) ? parsed : 0;
17970
+ }
17971
+ return 0;
17972
+ }
17973
+ formatFieldValue(value, format) {
17974
+ if (format == null)
17975
+ return value == null ? '' : String(value);
17976
+ const normalized = typeof format === 'string' ? { kind: format } : format;
17977
+ const kind = typeof normalized?.kind === 'string' ? normalized.kind.trim() : '';
17978
+ const locale = typeof normalized?.locale === 'string' && normalized.locale.trim() ? normalized.locale.trim() : 'pt-BR';
17979
+ switch (kind) {
17980
+ case 'integer':
17981
+ return new Intl.NumberFormat(locale, {
17982
+ maximumFractionDigits: 0,
17983
+ minimumFractionDigits: 0,
17984
+ }).format(this.toNumber(value));
17985
+ case 'number':
17986
+ return new Intl.NumberFormat(locale, {
17987
+ maximumFractionDigits: this.toNumber(normalized?.maximumFractionDigits),
17988
+ minimumFractionDigits: this.toNumber(normalized?.minimumFractionDigits),
17989
+ }).format(this.toNumber(value));
17990
+ case 'currency':
17991
+ return new Intl.NumberFormat(locale, {
17992
+ style: 'currency',
17993
+ currency: typeof normalized?.currency === 'string' && normalized.currency.trim() ? normalized.currency.trim() : 'BRL',
17994
+ maximumFractionDigits: normalized?.maximumFractionDigits ?? 2,
17995
+ minimumFractionDigits: normalized?.minimumFractionDigits ?? 2,
17996
+ }).format(this.toNumber(value));
17997
+ case 'percent':
17998
+ return new Intl.NumberFormat(locale, {
17999
+ style: 'percent',
18000
+ maximumFractionDigits: normalized?.maximumFractionDigits ?? 1,
18001
+ minimumFractionDigits: normalized?.minimumFractionDigits ?? 0,
18002
+ }).format(this.toNumber(value));
18003
+ default:
18004
+ return value == null ? '' : String(value);
18005
+ }
18006
+ }
15629
18007
  clone(value) {
15630
18008
  if (value == null || typeof value !== 'object')
15631
18009
  return value;
@@ -15664,6 +18042,7 @@ class DynamicWidgetPageComponent {
15664
18042
  componentInstanceId;
15665
18043
  pageChange = new EventEmitter();
15666
18044
  widgets = signal([], ...(ngDevMode ? [{ debugName: "widgets" }] : []));
18045
+ renderedGroups = signal([], ...(ngDevMode ? [{ debugName: "renderedGroups" }] : []));
15667
18046
  mergedContext = {};
15668
18047
  pageGap = '16px';
15669
18048
  gridTemplateColumns = 'minmax(0, 1fr)';
@@ -15677,6 +18056,9 @@ class DynamicWidgetPageComponent {
15677
18056
  diagnostics: [],
15678
18057
  };
15679
18058
  layout;
18059
+ grouping = [];
18060
+ pageColumnCount = 1;
18061
+ activeTabs = {};
15680
18062
  appliedPersisted = false;
15681
18063
  isHydrating = false;
15682
18064
  persistenceReady = false;
@@ -15700,17 +18082,16 @@ class DynamicWidgetPageComponent {
15700
18082
  ngOnChanges(changes) {
15701
18083
  if (changes['page'] || changes['context'] || changes['enableCustomization']) {
15702
18084
  const parsed = this.parsePage(this.page);
15703
- this.pageDefinition = parsed ? { ...parsed, state: this.stateRuntime.normalizeState(parsed.state) } : parsed;
18085
+ const resolvedPage = parsed ? this.resolvePagePresets(parsed) : parsed;
18086
+ this.pageDefinition = resolvedPage ? { ...resolvedPage, state: this.stateRuntime.normalizeState(resolvedPage.state) } : resolvedPage;
15704
18087
  this.pageRuntime = this.buildStateRuntime(this.pageDefinition?.state, this.pageDefinition?.context);
15705
18088
  this.pageState = this.pageRuntime.state;
15706
- const rawWidgets = parsed?.widgets || [];
18089
+ const rawWidgets = this.pageDefinition?.widgets || [];
15707
18090
  let widgets = this.applyEditShellActions(rawWidgets);
15708
- widgets = this.applyStateConnections(parsed?.connections, this.pageRuntime.effectiveValues, this.collectConnectedStatePaths(parsed?.connections), widgets);
18091
+ widgets = this.applyStateConnections(this.pageDefinition?.connections, this.pageRuntime.effectiveValues, this.collectConnectedStatePaths(this.pageDefinition?.connections), widgets);
15709
18092
  this.pageDefinition = this.pageDefinition ? { ...this.pageDefinition, widgets } : this.pageDefinition;
15710
18093
  this.page = this.pageDefinition;
15711
- this.widgets.set(widgets);
15712
- this.mergedContext = this.mergeContext(parsed?.context, this.context, this.pageRuntime);
15713
- this.applyLayout(parsed?.layout);
18094
+ this.applyResponsivePresentation(this.pageDefinition, widgets, this.pageRuntime);
15714
18095
  this.reportStateDiagnostics(this.pageRuntime.diagnostics);
15715
18096
  }
15716
18097
  if (this.autoPersist && (changes['pageIdentity'] || changes['componentInstanceId'])) {
@@ -15962,42 +18343,49 @@ class DynamicWidgetPageComponent {
15962
18343
  applyPageUpdate(next, persist = true, runtime = this.buildStateRuntime(next.state, next.context)) {
15963
18344
  const normalized = { ...next, state: runtime.state };
15964
18345
  const stateBoundWidgets = this.applyStateConnections(normalized.connections, runtime.effectiveValues, this.collectConnectedStatePaths(normalized.connections), normalized.widgets || []);
15965
- const hydrated = { ...normalized, widgets: stateBoundWidgets };
18346
+ const hydrated = this.resolvePagePresets({ ...normalized, widgets: stateBoundWidgets });
15966
18347
  this.pageDefinition = hydrated;
15967
18348
  this.page = hydrated;
15968
18349
  this.pageState = runtime.state;
15969
18350
  this.pageRuntime = runtime;
15970
- this.widgets.set(this.applyEditShellActions(stateBoundWidgets));
15971
- this.mergedContext = this.mergeContext(hydrated.context, this.context, runtime);
15972
- this.applyLayout(hydrated.layout);
18351
+ this.applyResponsivePresentation(hydrated, stateBoundWidgets, runtime);
15973
18352
  this.pageChange.emit(hydrated);
15974
18353
  this.reportStateDiagnostics(runtime.diagnostics);
15975
18354
  if (persist && this.autoPersist && !this.isHydrating) {
15976
18355
  this.savePage(hydrated);
15977
18356
  }
15978
18357
  }
15979
- applyLayout(layout) {
18358
+ applyLayout(layout, grouping) {
15980
18359
  this.layout = layout;
18360
+ this.grouping = grouping || [];
15981
18361
  const orientation = layout?.orientation || 'vertical';
15982
18362
  const columns = orientation === 'columns' ? this.resolveColumns(layout) : 1;
18363
+ this.pageColumnCount = columns;
15983
18364
  this.gridTemplateColumns = `repeat(${columns}, minmax(0, 1fr))`;
15984
18365
  this.pageGap = layout?.gap || '16px';
15985
18366
  }
15986
- mergeContext(pageContext, inputContext, runtime) {
18367
+ mergeContext(pageContext, inputContext, runtime, pageDefinition) {
15987
18368
  const baseContext = this.buildStateContext(pageContext);
18369
+ const themePresetId = pageDefinition?.themePreset || '';
18370
+ const themePreset = themePresetId ? BUILTIN_PAGE_THEME_PRESETS[themePresetId] : undefined;
18371
+ const layoutPresetId = pageDefinition?.layoutPreset || '';
18372
+ const layoutPreset = layoutPresetId ? BUILTIN_PAGE_LAYOUT_PRESETS[layoutPresetId] : undefined;
15988
18373
  return {
15989
18374
  ...baseContext,
15990
18375
  ...(inputContext || {}),
18376
+ pageDeviceKind: this.resolveDeviceKind(),
18377
+ pageLayoutPreset: layoutPreset || null,
18378
+ pageThemePreset: themePreset || null,
18379
+ pageGrouping: this.cloneGrouping(pageDefinition?.grouping),
15991
18380
  pageState: this.cloneStateValues(runtime.primaryValues),
15992
18381
  pageStateDerived: this.cloneStateValues(runtime.derivedValues),
15993
18382
  pageStateEffective: this.cloneStateValues(runtime.effectiveValues),
15994
18383
  };
15995
18384
  }
15996
18385
  onWindowResize() {
15997
- if (!this.layout || (this.layout.orientation || 'vertical') !== 'columns')
18386
+ if (!this.pageDefinition)
15998
18387
  return;
15999
- const columns = this.resolveColumns(this.layout);
16000
- this.gridTemplateColumns = `repeat(${columns}, minmax(0, 1fr))`;
18388
+ this.applyResponsivePresentation(this.pageDefinition, this.pageDefinition.widgets || [], this.pageRuntime);
16001
18389
  }
16002
18390
  resolveColumns(layout) {
16003
18391
  const base = Math.max(1, Number(layout?.columns) || 2);
@@ -16030,7 +18418,7 @@ class DynamicWidgetPageComponent {
16030
18418
  return this.pageDefinition;
16031
18419
  const parsed = this.parsePage(this.page);
16032
18420
  if (parsed)
16033
- return parsed;
18421
+ return this.resolvePagePresets(parsed);
16034
18422
  return { widgets: this.widgets(), state: this.stateRuntime.normalizeState(this.pageState) };
16035
18423
  }
16036
18424
  parsePage(input) {
@@ -16046,6 +18434,217 @@ class DynamicWidgetPageComponent {
16046
18434
  }
16047
18435
  return input;
16048
18436
  }
18437
+ resolvePagePresets(page) {
18438
+ const preset = page.layoutPreset ? BUILTIN_PAGE_LAYOUT_PRESETS[page.layoutPreset] : undefined;
18439
+ const themePresetId = page.themePreset || preset?.defaultThemePreset;
18440
+ const mergedLayout = this.mergeLayout(preset?.defaultLayout, page.layout);
18441
+ const grouping = (page.grouping && page.grouping.length ? page.grouping : preset?.defaultGrouping) || undefined;
18442
+ return {
18443
+ ...page,
18444
+ layout: mergedLayout,
18445
+ grouping,
18446
+ themePreset: themePresetId,
18447
+ };
18448
+ }
18449
+ mergeLayout(presetLayout, pageLayout) {
18450
+ if (!presetLayout && !pageLayout)
18451
+ return undefined;
18452
+ return {
18453
+ ...(presetLayout || {}),
18454
+ ...(pageLayout || {}),
18455
+ breakpoints: {
18456
+ ...(presetLayout?.breakpoints || {}),
18457
+ ...(pageLayout?.breakpoints || {}),
18458
+ },
18459
+ };
18460
+ }
18461
+ applyResponsivePresentation(pageDefinition, widgets, runtime) {
18462
+ const effective = this.resolveEffectivePresentation(pageDefinition, widgets);
18463
+ this.applyLayout(effective.layout, effective.grouping);
18464
+ this.syncActiveTabs(effective.groups);
18465
+ this.mergedContext = this.mergeContext(pageDefinition?.context, this.context, runtime, {
18466
+ ...(pageDefinition || { widgets: [] }),
18467
+ layout: effective.layout,
18468
+ grouping: effective.grouping,
18469
+ });
18470
+ this.renderedGroups.set(effective.groups);
18471
+ this.widgets.set(this.resolveShellTemplates(effective.widgets, runtime));
18472
+ }
18473
+ resolveEffectivePresentation(pageDefinition, widgets) {
18474
+ const variant = this.resolveDeviceVariant(pageDefinition?.deviceLayouts);
18475
+ const layout = this.mergeLayout(pageDefinition?.layout, variant?.layout);
18476
+ const grouping = this.applyGroupingOverrides(pageDefinition?.grouping, variant?.groupingOverrides);
18477
+ const widgetsWithOverrides = this.applyWidgetLayoutOverrides(this.applyEditShellActions(widgets), variant?.widgetOverrides);
18478
+ const groups = this.buildRenderedGroups(grouping, widgetsWithOverrides);
18479
+ return {
18480
+ layout,
18481
+ grouping,
18482
+ widgets: widgetsWithOverrides,
18483
+ groups,
18484
+ };
18485
+ }
18486
+ resolveDeviceVariant(deviceLayouts) {
18487
+ const kind = this.resolveDeviceKind();
18488
+ if (kind === 'mobile')
18489
+ return deviceLayouts?.mobile;
18490
+ if (kind === 'tablet')
18491
+ return deviceLayouts?.tablet;
18492
+ return deviceLayouts?.desktop;
18493
+ }
18494
+ resolveDeviceKind() {
18495
+ const width = typeof window !== 'undefined' ? window.innerWidth : 1440;
18496
+ if (width < 768)
18497
+ return 'mobile';
18498
+ if (width < 1280)
18499
+ return 'tablet';
18500
+ return 'desktop';
18501
+ }
18502
+ applyWidgetLayoutOverrides(widgets, overrides) {
18503
+ const rendered = widgets
18504
+ .map((widget) => {
18505
+ const override = overrides?.[widget.key];
18506
+ if (override?.hidden)
18507
+ return null;
18508
+ const renderClassName = this.mergeClassNames(widget.className, override?.className);
18509
+ return {
18510
+ ...widget,
18511
+ renderClassName,
18512
+ renderSpan: override?.span,
18513
+ __order: typeof override?.order === 'number' ? override.order : Number.MAX_SAFE_INTEGER,
18514
+ };
18515
+ })
18516
+ .filter((widget) => !!widget)
18517
+ .sort((left, right) => left.__order - right.__order);
18518
+ return rendered.map(({ __order, ...widget }) => widget);
18519
+ }
18520
+ applyGroupingOverrides(grouping, overrides) {
18521
+ if (!grouping?.length)
18522
+ return grouping;
18523
+ if (!overrides?.length)
18524
+ return this.cloneGrouping(grouping);
18525
+ const overridesById = new Map(overrides.map((item) => [item.id, item]));
18526
+ return this.cloneGrouping(grouping)
18527
+ .map((group) => {
18528
+ const override = overridesById.get(group.id);
18529
+ if (!override || override.hidden)
18530
+ return override?.hidden ? null : group;
18531
+ if (group.kind === 'tabs') {
18532
+ return {
18533
+ ...group,
18534
+ tabs: override.tabs?.length ? override.tabs : group.tabs,
18535
+ };
18536
+ }
18537
+ return {
18538
+ ...group,
18539
+ widgetKeys: override.widgetKeys?.length ? override.widgetKeys : group.widgetKeys,
18540
+ };
18541
+ })
18542
+ .filter((group) => !!group);
18543
+ }
18544
+ buildRenderedGroups(grouping, widgets) {
18545
+ if (!grouping?.length)
18546
+ return [];
18547
+ const widgetMap = new Map(widgets.map((widget) => [widget.key, widget]));
18548
+ const usedKeys = new Set();
18549
+ const groups = [];
18550
+ for (const group of grouping) {
18551
+ if (group.kind === 'tabs') {
18552
+ const tabs = group.tabs
18553
+ .map((tab) => {
18554
+ const tabWidgets = tab.widgetKeys
18555
+ .map((key) => widgetMap.get(key))
18556
+ .filter((widget) => !!widget);
18557
+ for (const widget of tabWidgets)
18558
+ usedKeys.add(widget.key);
18559
+ return { id: tab.id, label: tab.label, widgets: tabWidgets };
18560
+ })
18561
+ .filter((tab) => tab.widgets.length);
18562
+ if (tabs.length) {
18563
+ groups.push({
18564
+ id: group.id,
18565
+ kind: 'tabs',
18566
+ label: group.label,
18567
+ widgets: [],
18568
+ tabs,
18569
+ });
18570
+ }
18571
+ continue;
18572
+ }
18573
+ const groupWidgets = group.widgetKeys
18574
+ .map((key) => widgetMap.get(key))
18575
+ .filter((widget) => !!widget);
18576
+ for (const widget of groupWidgets)
18577
+ usedKeys.add(widget.key);
18578
+ if (!groupWidgets.length)
18579
+ continue;
18580
+ groups.push({
18581
+ id: group.id,
18582
+ kind: group.kind,
18583
+ label: 'label' in group ? group.label : undefined,
18584
+ layout: 'layout' in group ? group.layout : undefined,
18585
+ emphasis: 'emphasis' in group ? group.emphasis : undefined,
18586
+ side: 'side' in group ? group.side : undefined,
18587
+ widgets: groupWidgets,
18588
+ });
18589
+ }
18590
+ const ungrouped = widgets.filter((widget) => !usedKeys.has(widget.key));
18591
+ if (ungrouped.length) {
18592
+ groups.push({
18593
+ id: 'ungrouped',
18594
+ kind: 'ungrouped',
18595
+ label: undefined,
18596
+ layout: this.pageColumnCount > 1 ? 'grid' : 'stack',
18597
+ widgets: ungrouped,
18598
+ });
18599
+ }
18600
+ return groups;
18601
+ }
18602
+ syncActiveTabs(groups) {
18603
+ const next = { ...this.activeTabs };
18604
+ for (const group of groups) {
18605
+ if (group.kind !== 'tabs' || !group.tabs?.length)
18606
+ continue;
18607
+ const current = next[group.id];
18608
+ if (!current || !group.tabs.some((tab) => tab.id === current)) {
18609
+ next[group.id] = group.tabs[0].id;
18610
+ }
18611
+ }
18612
+ this.activeTabs = next;
18613
+ }
18614
+ activeTabId(groupId) {
18615
+ return this.activeTabs[groupId] || '';
18616
+ }
18617
+ selectGroupTab(groupId, tabId) {
18618
+ this.activeTabs = { ...this.activeTabs, [groupId]: tabId };
18619
+ }
18620
+ resolveGroupContentLayout(group) {
18621
+ if (group.kind === 'hero')
18622
+ return 'row';
18623
+ if (group.kind === 'rail')
18624
+ return 'stack';
18625
+ return group.layout || (this.pageColumnCount > 1 ? 'grid' : 'stack');
18626
+ }
18627
+ groupGridColumn(group) {
18628
+ if (group.kind === 'rail' && this.pageColumnCount > 1) {
18629
+ return `${this.pageColumnCount} / span 1`;
18630
+ }
18631
+ return '1 / -1';
18632
+ }
18633
+ widgetGridColumn(widget) {
18634
+ if (!widget.renderSpan || widget.renderSpan <= 1)
18635
+ return null;
18636
+ return `span ${widget.renderSpan}`;
18637
+ }
18638
+ widgetClassName(widget) {
18639
+ return widget.renderClassName || widget.className || '';
18640
+ }
18641
+ mergeClassNames(...classNames) {
18642
+ const merged = classNames
18643
+ .flatMap((value) => (value || '').split(/\s+/))
18644
+ .map((value) => value.trim())
18645
+ .filter(Boolean);
18646
+ return merged.length ? Array.from(new Set(merged)).join(' ') : undefined;
18647
+ }
16049
18648
  applyEditShellActions(widgets) {
16050
18649
  if (!this.enableCustomization)
16051
18650
  return widgets;
@@ -16173,6 +18772,9 @@ class DynamicWidgetPageComponent {
16173
18772
  cloneStateValues(state) {
16174
18773
  return state ? JSON.parse(JSON.stringify(state)) : {};
16175
18774
  }
18775
+ cloneGrouping(grouping) {
18776
+ return grouping ? JSON.parse(JSON.stringify(grouping)) : [];
18777
+ }
16176
18778
  collectConnectedStatePaths(connections) {
16177
18779
  return Array.from(new Set((connections || [])
16178
18780
  .filter((connection) => 'state' in connection.from)
@@ -16187,6 +18789,18 @@ class DynamicWidgetPageComponent {
16187
18789
  ...(this.context || {}),
16188
18790
  };
16189
18791
  }
18792
+ resolveShellTemplates(widgets, runtime) {
18793
+ const context = {
18794
+ ...(this.mergedContext || {}),
18795
+ state: this.cloneStateValues(runtime.effectiveValues),
18796
+ pageState: this.cloneStateValues(runtime.primaryValues),
18797
+ pageStateDerived: this.cloneStateValues(runtime.derivedValues),
18798
+ pageStateEffective: this.cloneStateValues(runtime.effectiveValues),
18799
+ };
18800
+ return widgets.map((widget) => (widget.shell
18801
+ ? { ...widget, shell: this.resolveTemplate(widget.shell, context) }
18802
+ : widget));
18803
+ }
16190
18804
  reportStateDiagnostics(diagnostics) {
16191
18805
  for (const diagnostic of diagnostics || []) {
16192
18806
  console.warn(`[DynamicWidgetPage] ${diagnostic}`);
@@ -16213,25 +18827,100 @@ class DynamicWidgetPageComponent {
16213
18827
  [style.gap]="pageGap"
16214
18828
  [style.gridTemplateColumns]="gridTemplateColumns"
16215
18829
  >
16216
- @for (w of widgets(); track w.key) {
16217
- <div class="pdx-widget" [class]="w.className || ''">
16218
- <praxis-widget-shell
16219
- [shell]="w.shell"
16220
- [context]="mergedContext"
16221
- (action)="onShellAction(w.key, $event)"
18830
+ @if (renderedGroups().length) {
18831
+ @for (group of renderedGroups(); track group.id) {
18832
+ <section
18833
+ class="pdx-group"
18834
+ [class]="'pdx-group--' + group.kind + (group.layout ? ' pdx-group--layout-' + group.layout : '') + (group.emphasis ? ' pdx-group--emphasis-' + group.emphasis : '') + (group.side ? ' pdx-group--side-' + group.side : '')"
18835
+ [style.gridColumn]="groupGridColumn(group)"
18836
+ [attr.data-group-id]="group.id"
18837
+ [attr.data-group-kind]="group.kind"
16222
18838
  >
16223
- <ng-container
16224
- [dynamicWidgetLoader]="w.definition"
18839
+ @if (group.label) {
18840
+ <header class="pdx-group-header">
18841
+ <div class="pdx-group-title">{{ group.label }}</div>
18842
+ </header>
18843
+ }
18844
+ @if (group.kind === 'tabs') {
18845
+ <div class="pdx-group-tabs">
18846
+ <div class="pdx-tabs-header">
18847
+ @for (tab of group.tabs || []; track tab.id) {
18848
+ <button
18849
+ class="pdx-tab-chip"
18850
+ type="button"
18851
+ [class.active]="activeTabId(group.id) === tab.id"
18852
+ (click)="selectGroupTab(group.id, tab.id)"
18853
+ >
18854
+ {{ tab.label || tab.id }}
18855
+ </button>
18856
+ }
18857
+ </div>
18858
+ @for (tab of group.tabs || []; track tab.id) {
18859
+ @if (activeTabId(group.id) === tab.id) {
18860
+ <div class="pdx-group-content" [class]="'pdx-group-content--' + resolveGroupContentLayout(group)">
18861
+ @for (w of tab.widgets; track w.key) {
18862
+ <div class="pdx-widget" [class]="w.renderClassName || w.className || ''" [style.gridColumn]="widgetGridColumn(w)">
18863
+ <praxis-widget-shell
18864
+ [shell]="w.shell"
18865
+ [context]="mergedContext"
18866
+ (action)="onShellAction(w.key, $event)"
18867
+ >
18868
+ <ng-container
18869
+ [dynamicWidgetLoader]="w.definition"
18870
+ [context]="mergedContext"
18871
+ [strictValidation]="strictValidation"
18872
+ (widgetEvent)="onWidgetEvent(w.key, $event)"
18873
+ ></ng-container>
18874
+ </praxis-widget-shell>
18875
+ </div>
18876
+ }
18877
+ </div>
18878
+ }
18879
+ }
18880
+ </div>
18881
+ } @else {
18882
+ <div class="pdx-group-content" [class]="'pdx-group-content--' + resolveGroupContentLayout(group)">
18883
+ @for (w of group.widgets; track w.key) {
18884
+ <div class="pdx-widget" [class]="widgetClassName(w)" [style.gridColumn]="widgetGridColumn(w)">
18885
+ <praxis-widget-shell
18886
+ [shell]="w.shell"
18887
+ [context]="mergedContext"
18888
+ (action)="onShellAction(w.key, $event)"
18889
+ >
18890
+ <ng-container
18891
+ [dynamicWidgetLoader]="w.definition"
18892
+ [context]="mergedContext"
18893
+ [strictValidation]="strictValidation"
18894
+ (widgetEvent)="onWidgetEvent(w.key, $event)"
18895
+ ></ng-container>
18896
+ </praxis-widget-shell>
18897
+ </div>
18898
+ }
18899
+ </div>
18900
+ }
18901
+ </section>
18902
+ }
18903
+ } @else {
18904
+ @for (w of widgets(); track w.key) {
18905
+ <div class="pdx-widget" [class]="widgetClassName(w)" [style.gridColumn]="widgetGridColumn(w)">
18906
+ <praxis-widget-shell
18907
+ [shell]="w.shell"
16225
18908
  [context]="mergedContext"
16226
- [strictValidation]="strictValidation"
16227
- (widgetEvent)="onWidgetEvent(w.key, $event)"
16228
- ></ng-container>
16229
- </praxis-widget-shell>
16230
- </div>
18909
+ (action)="onShellAction(w.key, $event)"
18910
+ >
18911
+ <ng-container
18912
+ [dynamicWidgetLoader]="w.definition"
18913
+ [context]="mergedContext"
18914
+ [strictValidation]="strictValidation"
18915
+ (widgetEvent)="onWidgetEvent(w.key, $event)"
18916
+ ></ng-container>
18917
+ </praxis-widget-shell>
18918
+ </div>
18919
+ }
16231
18920
  }
16232
18921
  </div>
16233
18922
  </div>
16234
- `, isInline: true, styles: [".pdx-page-wrapper{position:relative;display:block}.pdx-page{display:grid;gap:16px;grid-template-columns:minmax(0,1fr)}.pdx-page-settings{position:sticky;top:8px;z-index:2;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);color:inherit;border-radius:999px;margin-bottom:8px}.pdx-widget{position:relative;background:var(--pfx-surface, transparent);border-radius:8px}.pdx-page-wrapper.editing .pdx-widget-settings{opacity:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent"], exportAs: ["dynamicWidgetLoader"] }, { kind: "component", type: WidgetShellComponent, selector: "praxis-widget-shell", inputs: ["shell", "context"], outputs: ["action"] }] });
18923
+ `, isInline: true, styles: [".pdx-page-wrapper{position:relative;display:block}.pdx-page{display:grid;gap:16px;grid-template-columns:minmax(0,1fr)}.pdx-page-settings{position:sticky;top:8px;z-index:2;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);color:inherit;border-radius:999px;margin-bottom:8px}.pdx-widget{position:relative;background:var(--pfx-surface, transparent);border-radius:8px}.pdx-group{display:grid;gap:12px;grid-column:1 / -1;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 76%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 72%,transparent)}.pdx-group--hero{padding:16px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-primary-container) 42%,transparent),color-mix(in srgb,var(--md-sys-color-surface-container-low) 86%,transparent))}.pdx-group--rail{align-self:start}.pdx-group-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.pdx-group-title{font-size:.95rem;font-weight:600;color:var(--md-sys-color-on-surface)}.pdx-group-content{display:grid;gap:12px}.pdx-group-content--stack{grid-template-columns:minmax(0,1fr)}.pdx-group-content--row{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.pdx-group-content--grid{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.pdx-group-tabs{display:grid;gap:12px}.pdx-tabs-header{display:flex;flex-wrap:wrap;gap:8px}.pdx-tab-chip{appearance:none;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-lowest);color:var(--md-sys-color-on-surface);border-radius:999px;padding:8px 12px;font:inherit;cursor:pointer}.pdx-tab-chip.active{border-color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 78%,transparent);color:var(--md-sys-color-on-primary-container)}.pdx-page-wrapper.editing .pdx-widget-settings{opacity:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent"], exportAs: ["dynamicWidgetLoader"] }, { kind: "component", type: WidgetShellComponent, selector: "praxis-widget-shell", inputs: ["shell", "context"], outputs: ["action"] }] });
16235
18924
  }
16236
18925
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicWidgetPageComponent, decorators: [{
16237
18926
  type: Component,
@@ -16255,25 +18944,100 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
16255
18944
  [style.gap]="pageGap"
16256
18945
  [style.gridTemplateColumns]="gridTemplateColumns"
16257
18946
  >
16258
- @for (w of widgets(); track w.key) {
16259
- <div class="pdx-widget" [class]="w.className || ''">
16260
- <praxis-widget-shell
16261
- [shell]="w.shell"
16262
- [context]="mergedContext"
16263
- (action)="onShellAction(w.key, $event)"
18947
+ @if (renderedGroups().length) {
18948
+ @for (group of renderedGroups(); track group.id) {
18949
+ <section
18950
+ class="pdx-group"
18951
+ [class]="'pdx-group--' + group.kind + (group.layout ? ' pdx-group--layout-' + group.layout : '') + (group.emphasis ? ' pdx-group--emphasis-' + group.emphasis : '') + (group.side ? ' pdx-group--side-' + group.side : '')"
18952
+ [style.gridColumn]="groupGridColumn(group)"
18953
+ [attr.data-group-id]="group.id"
18954
+ [attr.data-group-kind]="group.kind"
16264
18955
  >
16265
- <ng-container
16266
- [dynamicWidgetLoader]="w.definition"
18956
+ @if (group.label) {
18957
+ <header class="pdx-group-header">
18958
+ <div class="pdx-group-title">{{ group.label }}</div>
18959
+ </header>
18960
+ }
18961
+ @if (group.kind === 'tabs') {
18962
+ <div class="pdx-group-tabs">
18963
+ <div class="pdx-tabs-header">
18964
+ @for (tab of group.tabs || []; track tab.id) {
18965
+ <button
18966
+ class="pdx-tab-chip"
18967
+ type="button"
18968
+ [class.active]="activeTabId(group.id) === tab.id"
18969
+ (click)="selectGroupTab(group.id, tab.id)"
18970
+ >
18971
+ {{ tab.label || tab.id }}
18972
+ </button>
18973
+ }
18974
+ </div>
18975
+ @for (tab of group.tabs || []; track tab.id) {
18976
+ @if (activeTabId(group.id) === tab.id) {
18977
+ <div class="pdx-group-content" [class]="'pdx-group-content--' + resolveGroupContentLayout(group)">
18978
+ @for (w of tab.widgets; track w.key) {
18979
+ <div class="pdx-widget" [class]="w.renderClassName || w.className || ''" [style.gridColumn]="widgetGridColumn(w)">
18980
+ <praxis-widget-shell
18981
+ [shell]="w.shell"
18982
+ [context]="mergedContext"
18983
+ (action)="onShellAction(w.key, $event)"
18984
+ >
18985
+ <ng-container
18986
+ [dynamicWidgetLoader]="w.definition"
18987
+ [context]="mergedContext"
18988
+ [strictValidation]="strictValidation"
18989
+ (widgetEvent)="onWidgetEvent(w.key, $event)"
18990
+ ></ng-container>
18991
+ </praxis-widget-shell>
18992
+ </div>
18993
+ }
18994
+ </div>
18995
+ }
18996
+ }
18997
+ </div>
18998
+ } @else {
18999
+ <div class="pdx-group-content" [class]="'pdx-group-content--' + resolveGroupContentLayout(group)">
19000
+ @for (w of group.widgets; track w.key) {
19001
+ <div class="pdx-widget" [class]="widgetClassName(w)" [style.gridColumn]="widgetGridColumn(w)">
19002
+ <praxis-widget-shell
19003
+ [shell]="w.shell"
19004
+ [context]="mergedContext"
19005
+ (action)="onShellAction(w.key, $event)"
19006
+ >
19007
+ <ng-container
19008
+ [dynamicWidgetLoader]="w.definition"
19009
+ [context]="mergedContext"
19010
+ [strictValidation]="strictValidation"
19011
+ (widgetEvent)="onWidgetEvent(w.key, $event)"
19012
+ ></ng-container>
19013
+ </praxis-widget-shell>
19014
+ </div>
19015
+ }
19016
+ </div>
19017
+ }
19018
+ </section>
19019
+ }
19020
+ } @else {
19021
+ @for (w of widgets(); track w.key) {
19022
+ <div class="pdx-widget" [class]="widgetClassName(w)" [style.gridColumn]="widgetGridColumn(w)">
19023
+ <praxis-widget-shell
19024
+ [shell]="w.shell"
16267
19025
  [context]="mergedContext"
16268
- [strictValidation]="strictValidation"
16269
- (widgetEvent)="onWidgetEvent(w.key, $event)"
16270
- ></ng-container>
16271
- </praxis-widget-shell>
16272
- </div>
19026
+ (action)="onShellAction(w.key, $event)"
19027
+ >
19028
+ <ng-container
19029
+ [dynamicWidgetLoader]="w.definition"
19030
+ [context]="mergedContext"
19031
+ [strictValidation]="strictValidation"
19032
+ (widgetEvent)="onWidgetEvent(w.key, $event)"
19033
+ ></ng-container>
19034
+ </praxis-widget-shell>
19035
+ </div>
19036
+ }
16273
19037
  }
16274
19038
  </div>
16275
19039
  </div>
16276
- `, styles: [".pdx-page-wrapper{position:relative;display:block}.pdx-page{display:grid;gap:16px;grid-template-columns:minmax(0,1fr)}.pdx-page-settings{position:sticky;top:8px;z-index:2;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);color:inherit;border-radius:999px;margin-bottom:8px}.pdx-widget{position:relative;background:var(--pfx-surface, transparent);border-radius:8px}.pdx-page-wrapper.editing .pdx-widget-settings{opacity:1}\n"] }]
19040
+ `, styles: [".pdx-page-wrapper{position:relative;display:block}.pdx-page{display:grid;gap:16px;grid-template-columns:minmax(0,1fr)}.pdx-page-settings{position:sticky;top:8px;z-index:2;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);color:inherit;border-radius:999px;margin-bottom:8px}.pdx-widget{position:relative;background:var(--pfx-surface, transparent);border-radius:8px}.pdx-group{display:grid;gap:12px;grid-column:1 / -1;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 76%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 72%,transparent)}.pdx-group--hero{padding:16px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-primary-container) 42%,transparent),color-mix(in srgb,var(--md-sys-color-surface-container-low) 86%,transparent))}.pdx-group--rail{align-self:start}.pdx-group-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.pdx-group-title{font-size:.95rem;font-weight:600;color:var(--md-sys-color-on-surface)}.pdx-group-content{display:grid;gap:12px}.pdx-group-content--stack{grid-template-columns:minmax(0,1fr)}.pdx-group-content--row{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.pdx-group-content--grid{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.pdx-group-tabs{display:grid;gap:12px}.pdx-tabs-header{display:flex;flex-wrap:wrap;gap:8px}.pdx-tab-chip{appearance:none;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-lowest);color:var(--md-sys-color-on-surface);border-radius:999px;padding:8px 12px;font:inherit;cursor:pointer}.pdx-tab-chip.active{border-color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 78%,transparent);color:var(--md-sys-color-on-primary-container)}.pdx-page-wrapper.editing .pdx-widget-settings{opacity:1}\n"] }]
16277
19041
  }], ctorParameters: () => [{ type: ConnectionManagerService }, { type: WidgetPageStateRuntimeService }, { type: undefined, decorators: [{
16278
19042
  type: Optional
16279
19043
  }, {
@@ -16649,6 +19413,92 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
16649
19413
  type: Input
16650
19414
  }] } });
16651
19415
 
19416
+ class PraxisSurfaceHostComponent {
19417
+ title;
19418
+ subtitle;
19419
+ icon;
19420
+ widget;
19421
+ context = null;
19422
+ strictValidation = true;
19423
+ /**
19424
+ * Keep disabled by default to avoid duplicating the title already shown by
19425
+ * modal/drawer hosts. Inline consumers may opt into rendering it again.
19426
+ */
19427
+ renderTitleInsideBody = false;
19428
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisSurfaceHostComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
19429
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisSurfaceHostComponent, isStandalone: true, selector: "praxis-surface-host", inputs: { title: "title", subtitle: "subtitle", icon: "icon", widget: "widget", context: "context", strictValidation: "strictValidation", renderTitleInsideBody: "renderTitleInsideBody" }, ngImport: i0, template: `
19430
+ <div class="pdx-surface-host">
19431
+ @if (subtitle || (title && renderTitleInsideBody)) {
19432
+ <header class="pdx-surface-host__header">
19433
+ @if (icon) {
19434
+ <span class="material-icons pdx-surface-host__icon" aria-hidden="true">{{ icon }}</span>
19435
+ }
19436
+ <div class="pdx-surface-host__copy">
19437
+ @if (title && renderTitleInsideBody) {
19438
+ <h2 class="pdx-surface-host__title">{{ title }}</h2>
19439
+ }
19440
+ @if (subtitle) {
19441
+ <p class="pdx-surface-host__subtitle">{{ subtitle }}</p>
19442
+ }
19443
+ </div>
19444
+ </header>
19445
+ }
19446
+
19447
+ <section class="pdx-surface-host__body">
19448
+ <ng-container
19449
+ [dynamicWidgetLoader]="widget"
19450
+ [context]="context"
19451
+ [strictValidation]="strictValidation"
19452
+ ></ng-container>
19453
+ </section>
19454
+ </div>
19455
+ `, isInline: true, styles: [":host{display:block;min-width:0}.pdx-surface-host{display:grid;gap:16px;min-width:0}.pdx-surface-host__header{display:grid;grid-template-columns:auto 1fr;gap:12px;align-items:start}.pdx-surface-host__icon{font-size:20px;line-height:20px;color:var(--md-sys-color-primary, currentColor)}.pdx-surface-host__copy{display:grid;gap:4px;min-width:0}.pdx-surface-host__title,.pdx-surface-host__subtitle{margin:0}.pdx-surface-host__title{font-size:1rem;font-weight:600}.pdx-surface-host__subtitle{color:var(--md-sys-color-on-surface-variant, rgba(0, 0, 0, .64));font-size:.9375rem}.pdx-surface-host__body{min-width:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent"], exportAs: ["dynamicWidgetLoader"] }] });
19456
+ }
19457
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisSurfaceHostComponent, decorators: [{
19458
+ type: Component,
19459
+ args: [{ selector: 'praxis-surface-host', standalone: true, imports: [CommonModule, DynamicWidgetLoaderDirective], template: `
19460
+ <div class="pdx-surface-host">
19461
+ @if (subtitle || (title && renderTitleInsideBody)) {
19462
+ <header class="pdx-surface-host__header">
19463
+ @if (icon) {
19464
+ <span class="material-icons pdx-surface-host__icon" aria-hidden="true">{{ icon }}</span>
19465
+ }
19466
+ <div class="pdx-surface-host__copy">
19467
+ @if (title && renderTitleInsideBody) {
19468
+ <h2 class="pdx-surface-host__title">{{ title }}</h2>
19469
+ }
19470
+ @if (subtitle) {
19471
+ <p class="pdx-surface-host__subtitle">{{ subtitle }}</p>
19472
+ }
19473
+ </div>
19474
+ </header>
19475
+ }
19476
+
19477
+ <section class="pdx-surface-host__body">
19478
+ <ng-container
19479
+ [dynamicWidgetLoader]="widget"
19480
+ [context]="context"
19481
+ [strictValidation]="strictValidation"
19482
+ ></ng-container>
19483
+ </section>
19484
+ </div>
19485
+ `, styles: [":host{display:block;min-width:0}.pdx-surface-host{display:grid;gap:16px;min-width:0}.pdx-surface-host__header{display:grid;grid-template-columns:auto 1fr;gap:12px;align-items:start}.pdx-surface-host__icon{font-size:20px;line-height:20px;color:var(--md-sys-color-primary, currentColor)}.pdx-surface-host__copy{display:grid;gap:4px;min-width:0}.pdx-surface-host__title,.pdx-surface-host__subtitle{margin:0}.pdx-surface-host__title{font-size:1rem;font-weight:600}.pdx-surface-host__subtitle{color:var(--md-sys-color-on-surface-variant, rgba(0, 0, 0, .64));font-size:.9375rem}.pdx-surface-host__body{min-width:0}\n"] }]
19486
+ }], propDecorators: { title: [{
19487
+ type: Input
19488
+ }], subtitle: [{
19489
+ type: Input
19490
+ }], icon: [{
19491
+ type: Input
19492
+ }], widget: [{
19493
+ type: Input
19494
+ }], context: [{
19495
+ type: Input
19496
+ }], strictValidation: [{
19497
+ type: Input
19498
+ }], renderTitleInsideBody: [{
19499
+ type: Input
19500
+ }] } });
19501
+
16652
19502
  class EmptyStateCardComponent {
16653
19503
  icon = 'tab';
16654
19504
  title = 'Sem configuração disponível';
@@ -16686,7 +19536,7 @@ class EmptyStateCardComponent {
16686
19536
  </div>
16687
19537
  </mat-card-content>
16688
19538
  </mat-card>
16689
- `, isInline: true, styles: [".empty-card{display:block;margin:12px;border-color:var(--md-sys-color-outline-variant);--empty-icon-color: var(--md-sys-color-on-surface-variant)}.empty-card.empty-inline{margin:8px 0}.content{display:flex;align-items:center;gap:12px}.icon{font-size:32px;width:32px;height:32px;color:var(--empty-icon-color)}.texts{display:grid;gap:4px}.title{margin:0;font-size:16px;font-weight:600;color:var(--md-sys-color-on-surface)}.desc{margin:0;color:var(--md-sys-color-on-surface-variant)}.actions{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}.empty-card.tone-primary{--empty-icon-color: var(--md-sys-color-primary)}.empty-card.tone-secondary{--empty-icon-color: var(--md-sys-color-secondary)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i3.MatCardContent, selector: "mat-card-content" }, { 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: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
19539
+ `, isInline: true, styles: [".empty-card{display:block;margin:12px;border-color:var(--md-sys-color-outline-variant);--empty-icon-color: var(--md-sys-color-on-surface-variant)}.empty-card.empty-inline{margin:8px 0}.content{display:flex;align-items:center;gap:12px}.icon{font-size:32px;width:32px;height:32px;color:var(--empty-icon-color)}.texts{display:grid;gap:4px}.title{margin:0;font-size:16px;font-weight:600;color:var(--md-sys-color-on-surface)}.desc{margin:0;color:var(--md-sys-color-on-surface-variant)}.actions{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}.empty-card.tone-primary{--empty-icon-color: var(--md-sys-color-primary)}.empty-card.tone-secondary{--empty-icon-color: var(--md-sys-color-secondary)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i3$1.MatCardContent, selector: "mat-card-content" }, { 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: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
16690
19540
  }
16691
19541
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: EmptyStateCardComponent, decorators: [{
16692
19542
  type: Component,
@@ -16791,7 +19641,7 @@ class ResourceQuickConnectComponent {
16791
19641
  Rota normalizada: {{ normalizedPath() }}
16792
19642
  </small>
16793
19643
  </div>
16794
- `, isInline: true, styles: [".pdx-quick-connect{display:grid;gap:12px;padding:12px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface)}.pdx-quick-connect__hint{color:var(--md-sys-color-on-surface-variant);font-size:.9rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$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: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4$2.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: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
19644
+ `, isInline: true, styles: [".pdx-quick-connect{display:grid;gap:12px;padding:12px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface)}.pdx-quick-connect__hint{color:var(--md-sys-color-on-surface-variant);font-size:.9rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { 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: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
16795
19645
  }
16796
19646
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ResourceQuickConnectComponent, decorators: [{
16797
19647
  type: Component,
@@ -16961,7 +19811,7 @@ class PraxisIconPickerComponent {
16961
19811
  return { family: 'mss', name };
16962
19812
  return { family: 'mi', name };
16963
19813
  }
16964
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisIconPickerComponent, deps: [{ token: MAT_DIALOG_DATA, optional: true }, { token: i1$2.MatDialogRef, optional: true }], target: i0.ɵɵFactoryTarget.Component });
19814
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisIconPickerComponent, deps: [{ token: MAT_DIALOG_DATA, optional: true }, { token: i1$3.MatDialogRef, optional: true }], target: i0.ɵɵFactoryTarget.Component });
16965
19815
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisIconPickerComponent, isStandalone: true, selector: "praxis-icon-picker", inputs: { value: "value", icons: "icons" }, outputs: { selected: "selected", cancel: "cancel" }, host: { listeners: { "document:keydown.escape": "onEsc($event)", "document:keydown": "onGlobalKey($event)" } }, viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true }], ngImport: i0, template: `
16966
19816
  <div class="pip-root">
16967
19817
  <div class="pip-head">
@@ -17019,7 +19869,7 @@ class PraxisIconPickerComponent {
17019
19869
  <span class="pip-typed" *ngIf="query.trim()">{{ previewValue() }}</span>
17020
19870
  </div>
17021
19871
  </div>
17022
- `, isInline: true, styles: [".pip-root{display:flex;flex-direction:column;min-width:340px;max-width:760px;max-height:80vh;overflow:hidden;padding:16px;color:var(--md-sys-color-on-surface)}.pip-head{display:flex;gap:12px;align-items:center;margin-bottom:12px;flex-wrap:wrap}.pip-search{flex:1;min-width:180px}.pip-spacer{flex:1}.pip-body{flex:1;overflow:auto;display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px;padding:4px}.pip-item{display:flex;gap:10px;align-items:center;justify-content:flex-start;padding:10px;border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;background:var(--md-sys-color-surface);color:inherit;cursor:pointer;transition:background-color .18s ease,border-color .18s ease,box-shadow .18s ease}.pip-item:hover{background:var(--md-sys-color-surface-container)}.pip-item:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pip-item.selected{border-color:var(--md-sys-color-primary);box-shadow:0 0 0 2px var(--md-sys-color-primary)}.pip-name{font-size:12px;color:var(--md-sys-color-on-surface-variant);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.pip-hint{font-size:12px;color:var(--md-sys-color-on-surface-variant);margin:-6px 0 8px}.pip-stats{display:flex;justify-content:space-between;gap:8px;font-size:12px;color:var(--md-sys-color-on-surface-variant);margin-bottom:8px}.pip-shortcut{opacity:.9}.pip-empty{padding:12px;border:1px dashed var(--md-sys-color-outline-variant);border-radius:10px;color:var(--md-sys-color-on-surface-variant);background:var(--md-sys-color-surface-container);margin-top:8px}.pip-footer{display:flex;gap:12px;align-items:center;margin-top:10px}.pip-typed{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;color:var(--md-sys-color-on-surface)}.pip-family{display:flex;align-items:center}.pip-family .mat-mdc-chip-listbox{min-width:260px}.pip-root .mat-icon{font-family:Material Icons,Material Symbols Outlined,Material Symbols Rounded,Material Symbols Sharp!important;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24;color:currentColor}.material-symbols-outlined{font-family:Material Symbols Outlined;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}.material-symbols-rounded{font-family:Material Symbols Rounded;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}.material-symbols-sharp{font-family:Material Symbols Sharp;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$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: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: MatInputModule }, { kind: "directive", type: i4$2.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: i4.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: "ngmodule", type: MatChipsModule }, { kind: "component", type: i8.MatChipListbox, selector: "mat-chip-listbox", inputs: ["multiple", "aria-orientation", "selectable", "compareWith", "required", "hideSingleSelectionIndicator", "value"], outputs: ["change"] }, { kind: "component", type: i8.MatChipOption, selector: "mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]", inputs: ["selectable", "selected"], outputs: ["selectionChange"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
19872
+ `, isInline: true, styles: [".pip-root{display:flex;flex-direction:column;min-width:340px;max-width:760px;max-height:80vh;overflow:hidden;padding:16px;color:var(--md-sys-color-on-surface)}.pip-head{display:flex;gap:12px;align-items:center;margin-bottom:12px;flex-wrap:wrap}.pip-search{flex:1;min-width:180px}.pip-spacer{flex:1}.pip-body{flex:1;overflow:auto;display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px;padding:4px}.pip-item{display:flex;gap:10px;align-items:center;justify-content:flex-start;padding:10px;border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;background:var(--md-sys-color-surface);color:inherit;cursor:pointer;transition:background-color .18s ease,border-color .18s ease,box-shadow .18s ease}.pip-item:hover{background:var(--md-sys-color-surface-container)}.pip-item:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pip-item.selected{border-color:var(--md-sys-color-primary);box-shadow:0 0 0 2px var(--md-sys-color-primary)}.pip-name{font-size:12px;color:var(--md-sys-color-on-surface-variant);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.pip-hint{font-size:12px;color:var(--md-sys-color-on-surface-variant);margin:-6px 0 8px}.pip-stats{display:flex;justify-content:space-between;gap:8px;font-size:12px;color:var(--md-sys-color-on-surface-variant);margin-bottom:8px}.pip-shortcut{opacity:.9}.pip-empty{padding:12px;border:1px dashed var(--md-sys-color-outline-variant);border-radius:10px;color:var(--md-sys-color-on-surface-variant);background:var(--md-sys-color-surface-container);margin-top:8px}.pip-footer{display:flex;gap:12px;align-items:center;margin-top:10px}.pip-typed{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;color:var(--md-sys-color-on-surface)}.pip-family{display:flex;align-items:center}.pip-family .mat-mdc-chip-listbox{min-width:260px}.pip-root .mat-icon{font-family:Material Icons,Material Symbols Outlined,Material Symbols Rounded,Material Symbols Sharp!important;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24;color:currentColor}.material-symbols-outlined{font-family:Material Symbols Outlined;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}.material-symbols-rounded{font-family:Material Symbols Rounded;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}.material-symbols-sharp{font-family:Material Symbols Sharp;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { 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: i4.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: "ngmodule", type: MatChipsModule }, { kind: "component", type: i8.MatChipListbox, selector: "mat-chip-listbox", inputs: ["multiple", "aria-orientation", "selectable", "compareWith", "required", "hideSingleSelectionIndicator", "value"], outputs: ["change"] }, { kind: "component", type: i8.MatChipOption, selector: "mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]", inputs: ["selectable", "selected"], outputs: ["selectionChange"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
17023
19873
  }
17024
19874
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisIconPickerComponent, decorators: [{
17025
19875
  type: Component,
@@ -17086,7 +19936,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
17086
19936
  }, {
17087
19937
  type: Inject,
17088
19938
  args: [MAT_DIALOG_DATA]
17089
- }] }, { type: i1$2.MatDialogRef, decorators: [{
19939
+ }] }, { type: i1$3.MatDialogRef, decorators: [{
17090
19940
  type: Optional
17091
19941
  }] }], propDecorators: { value: [{
17092
19942
  type: Input
@@ -17363,7 +20213,7 @@ class SchemaViewerComponent {
17363
20213
  </mat-tab-group>
17364
20214
  </mat-card-content>
17365
20215
  </mat-card>
17366
- `, isInline: true, styles: [".schema-viewer{display:block;border-color:var(--md-sys-color-outline-variant)}.header{display:flex;align-items:center;justify-content:space-between;gap:8px}.title{display:flex;align-items:center;gap:8px}.title h3{margin:0;font-weight:600;color:var(--md-sys-color-on-surface)}.notes{margin-top:6px;color:var(--md-sys-color-on-surface-variant);font-size:12px}.copy-status{margin-top:6px;font-size:12px;color:var(--md-sys-color-primary)}.section{padding:12px}.section-actions{display:flex;justify-content:flex-end;margin-bottom:8px}.sub{font-weight:600;margin:12px 0 4px;color:var(--md-sys-color-on-surface)}.pretty{background:var(--md-sys-color-surface-container);padding:10px;border-radius:8px;overflow:auto;border:1px solid var(--md-sys-color-outline-variant)}.kv{display:grid;grid-template-columns:140px 1fr;align-items:baseline;gap:8px;margin:2px 0}.kv>span{color:var(--md-sys-color-on-surface-variant)}.kv>code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.muted{color:var(--md-sys-color-on-surface-variant)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i2$2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i2$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i3.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i3.MatCardHeader, selector: "mat-card-header" }, { 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: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
20216
+ `, isInline: true, styles: [".schema-viewer{display:block;border-color:var(--md-sys-color-outline-variant)}.header{display:flex;align-items:center;justify-content:space-between;gap:8px}.title{display:flex;align-items:center;gap:8px}.title h3{margin:0;font-weight:600;color:var(--md-sys-color-on-surface)}.notes{margin-top:6px;color:var(--md-sys-color-on-surface-variant);font-size:12px}.copy-status{margin-top:6px;font-size:12px;color:var(--md-sys-color-primary)}.section{padding:12px}.section-actions{display:flex;justify-content:flex-end;margin-bottom:8px}.sub{font-weight:600;margin:12px 0 4px;color:var(--md-sys-color-on-surface)}.pretty{background:var(--md-sys-color-surface-container);padding:10px;border-radius:8px;overflow:auto;border:1px solid var(--md-sys-color-outline-variant)}.kv{display:grid;grid-template-columns:140px 1fr;align-items:baseline;gap:8px;margin:2px 0}.kv>span{color:var(--md-sys-color-on-surface-variant)}.kv>code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.muted{color:var(--md-sys-color-on-surface-variant)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i2$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i2$1.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i3$1.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i3$1.MatCardHeader, selector: "mat-card-header" }, { 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: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: i1$2.JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
17367
20217
  }
17368
20218
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SchemaViewerComponent, decorators: [{
17369
20219
  type: Component,
@@ -18129,5 +20979,5 @@ function provideHookWhitelist(allowed) {
18129
20979
  * Generated bundle index. Do not edit.
18130
20980
  */
18131
20981
 
18132
- export { API_CONFIG_STORAGE_OPTIONS, API_URL, ASYNC_CONFIG_STORAGE, AllowedFileTypes, ApiConfigStorage, ApiEndpoint, BUILTIN_SHELL_PRESETS, CONFIG_STORAGE, CONNECTION_STORAGE, ComponentKeyService, ComponentMetadataRegistry, ConnectionManagerService, ConsoleLoggerSink, DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP, DEFAULT_TABLE_CONFIG, DYNAMIC_PAGE_AI_CAPABILITIES, DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK, DYNAMIC_PAGE_CONFIG_EDITOR, DYNAMIC_PAGE_SHELL_EDITOR, DefaultLoadingRenderer, DeferredAsyncConfigStorage, DynamicFormService, DynamicGridPageComponent, DynamicWidgetLoaderDirective, DynamicWidgetPageComponent, EDITORIAL_ALLOWED_CONTENT_FORMATS, EDITORIAL_COMPLIANCE_PRESETS, EDITORIAL_EXTERNAL_LINK_REL, EDITORIAL_FORM_TEMPLATE_CATALOG, EDITORIAL_HTML_ENABLED, EDITORIAL_MARKDOWN_IMAGES_ENABLED, EDITORIAL_SOLUTION_CATALOG, EDITORIAL_SOLUTION_PRESETS, EDITORIAL_THEME_PRESETS, EDITORIAL_WIDGET_CONVENTION_INPUTS, EDITORIAL_WIDGET_TAG, EMPLOYEE_ONBOARDING_EDITORIAL_SOLUTION, EMPLOYEE_ONBOARDING_EDITORIAL_TEMPLATE, EMPLOYEE_ONBOARDING_GUIDED_EDITORIAL_SOLUTION, EMPLOYEE_ONBOARDING_GUIDED_EDITORIAL_TEMPLATE, EVENT_REGISTRATION_EDITORIAL_SOLUTION, EVENT_REGISTRATION_EDITORIAL_TEMPLATE, EmptyStateCardComponent, ErrorMessageService, FIELD_METADATA_CAPABILITIES, FIELD_SELECTOR_REGISTRY_BASE, FIELD_SELECTOR_REGISTRY_DISABLE_DEFAULTS, FIELD_SELECTOR_REGISTRY_OVERRIDES, FORM_HOOKS, FORM_HOOKS_PRESETS, FORM_HOOKS_WHITELIST, FORM_HOOK_RESOLVERS, FieldControlType, FieldDataType, FieldSelectorRegistry, FormHooksRegistry, GLOBAL_ACTION_CATALOG$1 as GLOBAL_ACTION_CATALOG, GLOBAL_ACTION_HANDLERS, GLOBAL_ACTION_CATALOG as GLOBAL_ACTION_SPEC_CATALOG, GLOBAL_ACTION_UI_SCHEMAS, GLOBAL_ANALYTICS_SERVICE, GLOBAL_API_CLIENT, GLOBAL_CONFIG, GLOBAL_DIALOG_SERVICE, GLOBAL_ROUTE_GUARD_RESOLVER, GLOBAL_TOAST_SERVICE, GenericCrudService, GlobalActionService, GlobalConfigService, INLINE_FILTER_ALIAS_TOKENS, INLINE_FILTER_CONTROL_TYPES, INLINE_FILTER_CONTROL_TYPE_SET, INLINE_FILTER_CONTROL_TYPE_VALUES, INLINE_FILTER_TOKEN_TO_BASE_CONTROL_TYPE, INLINE_FILTER_TOKEN_TO_CONTROL_TYPE, IconPickerService, IconPosition, IconSize, LOGGER_LEVEL_BY_ENV, LOGGER_LEVEL_PRIORITY, LoadingOrchestrator, LocalConnectionStorage, LocalStorageAsyncAdapter, LocalStorageCacheAdapter, LocalStorageConfigService, LoggerService, LoggerThrottleTracker, LoggerWarnOnceTracker, MemoryCacheAdapter, NumericFormat, OVERLAY_DECIDER_DEBUG, OVERLAY_DECISION_MATRIX, ObservabilityDashboardService, OverlayDeciderService, PRAXIS_CORPORATE_SENSITIVE_KEYS, PRAXIS_DEFAULT_OBSERVABILITY_ALERT_RULES, PRAXIS_DYNAMIC_PAGE_COMPONENT_METADATA, PRAXIS_FOOTER_LINKS_METADATA, PRAXIS_GLOBAL_ACTION_CATALOG, PRAXIS_GLOBAL_CONFIG_BOOTSTRAP_OPTIONS, PRAXIS_GLOBAL_CONFIG_BOOTSTRAP_READY, PRAXIS_GLOBAL_CONFIG_TENANT_RESOLVER, PRAXIS_HERO_BANNER_METADATA, PRAXIS_I18N_CONFIG, PRAXIS_I18N_TRANSLATOR, PRAXIS_LEGAL_NOTICE_METADATA, PRAXIS_LOADING_CTX, PRAXIS_LOADING_RENDERER, PRAXIS_LOGGER_CONFIG, PRAXIS_LOGGER_SINKS, PRAXIS_OBSERVABILITY_DASHBOARD_OPTIONS, PRAXIS_RICH_TEXT_BLOCK_METADATA, PRAXIS_TELEMETRY_TRANSPORT, PRAXIS_USER_CONTEXT_SUMMARY_METADATA, PRIVACY_CONSENT_EDITORIAL_SOLUTION, PRIVACY_CONSENT_EDITORIAL_TEMPLATE, PraxisCore, PraxisFooterLinksComponent, PraxisGlobalErrorHandler, PraxisHeroBannerComponent, PraxisI18nService, PraxisIconDirective, PraxisIconPickerComponent, PraxisLegalNoticeComponent, PraxisLoadingInterceptor, PraxisRichTextBlockComponent, PraxisUserContextSummaryComponent, RULE_PROPERTY_SCHEMA, RemoteConfigStorage, ResourceQuickConnectComponent, SCHEMA_VIEWER_CONTEXT, SETTINGS_PANEL_BRIDGE, SETTINGS_PANEL_DATA, STEPPER_CONFIG_EDITOR, SchemaMetadataClient, SchemaNormalizerService, SchemaViewerComponent, TABLE_CONFIG_EDITOR, TableConfigService, TelemetryLoggerSink, TelemetryService, ValidationPattern, WidgetPageStateRuntimeService, WidgetShellComponent, applyLocalCustomizations$2 as applyLocalCustomizations, applyLocalCustomizations$1 as applyLocalFormCustomizations, buildAngularValidators, buildApiUrl, buildBaseColumnFromDef, buildBaseFormField, buildFormConfigFromEditorialTemplate, buildHeaders, buildPageKey, buildSchemaId, buildValidatorsFromValidatorOptions, cancelIfCpfInvalidHook, clampRange, cloneTableConfig, cnpjAlphaValidator, collapseWhitespace, composeHeadersWithVersion, conditionalAsyncValidator, convertFormLayoutToConfig, createCorporateLoggerConfig, createCorporateObservabilityOptions, createCpfCnpjValidator, createDefaultFormConfig, createDefaultTableConfig, createEmptyFormConfig, createPersistedPage, customAsyncValidatorFn, customValidatorFn, debounceAsyncValidator, deepMerge, ensureIds, ensureNoConflictsHookFactory, ensurePageIds, extractNormalizedError, fetchWithETag, fileTypeValidator, fillUndefined, generateId, getDefaultFormHints, getEditorialCompliancePresetById, getEditorialFormTemplateById, getEditorialFormTemplateCatalog, getEditorialSolutionById, getEditorialSolutionCatalog, getEditorialSolutionPresetById, getEditorialThemePresetById, getEssentialConfig, getFieldMetadataCapabilities, getGlobalActionCatalog, getGlobalActionUiSchema, getReferencedFieldMetadata, getTextTransformer, interpolatePraxisTranslation, isAllowedEditorialContentFormat, isAllowedEditorialHref, isCssTextTransform, isEditorialComponentMeta, isInlineFilterControlType, isRangeValidForFilter, isTableConfigV2, isValidFormConfig, isValidTableConfig, legacyCnpjValidator, legacyCpfValidator, logOnErrorHook, mapFieldDefinitionToMetadata, mapFieldDefinitionsToMetadata, matchFieldValidator, maxFileSizeValidator, mergeFieldMetadata, mergePraxisI18nConfigs, mergeTableConfigs, migrateFormLayoutRule, minWordsValidator, normalizeControlTypeKey, normalizeControlTypeToken, normalizeEditorialLink, normalizeEnd, normalizeFieldConstraints, normalizeFormConfig, normalizeFormMetadata, normalizePath, normalizeStart, normalizeUnknownError, notifySuccessHook, parseJsonResponseOrEmpty, praxisLoadingInterceptorFn, prefillFromContextHook, provideDefaultFormHooks, provideFieldSelectorRegistryBase, provideFieldSelectorRegistryOverride, provideFieldSelectorRegistryRuntime, provideFormHookPresets, provideFormHooks, provideGlobalActionCatalog, provideGlobalActionHandler, provideGlobalConfig, provideGlobalConfigReady, provideGlobalConfigSeed, provideGlobalConfigTenant, provideHookResolvers, provideHookWhitelist, provideOverlayDecisionMatrix, providePraxisAnalyticsGlobalActions, providePraxisDynamicPageMetadata, providePraxisFooterLinksMetadata, providePraxisGlobalActionCatalog, providePraxisGlobalActions, providePraxisGlobalConfigBootstrap, providePraxisHeroBannerMetadata, providePraxisHttpLoading, providePraxisI18n, providePraxisI18nConfig, providePraxisI18nTranslator, providePraxisLegalNoticeMetadata, providePraxisLoadingDefaults, providePraxisLogging, providePraxisRichTextBlockMetadata, providePraxisToastGlobalActions, providePraxisUserContextSummaryMetadata, provideRemoteGlobalConfig, reconcileFilterConfig, reconcileFormConfig, reconcileTableConfig, removeDiacritics, reportTelemetryHookFactory, requiredCheckedValidator, resolveBuiltinPresets, resolveControlTypeAlias, resolveHidden, resolveInlineFilterControlType, resolveInlineFilterControlTypeToBaseControlType, resolveLoggerConfig, resolveObservabilityOptions, resolveOffset, resolveOrder, resolveSpan, slugify, stripMasksHook, syncWithServerMetadata, toCamel, toCapitalize, toKebab, toPascal, toSentenceCase, toSnake, toTitleCase, trim, uniqueAsyncValidator, urlValidator, withMessage, withPraxisHttpLoading };
20982
+ export { API_CONFIG_STORAGE_OPTIONS, API_URL, ASYNC_CONFIG_STORAGE, AllowedFileTypes, ApiConfigStorage, ApiEndpoint, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, BUILTIN_SHELL_PRESETS, CONFIG_STORAGE, CONNECTION_STORAGE, ComponentKeyService, ComponentMetadataRegistry, ConnectionManagerService, ConsoleLoggerSink, DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP, DEFAULT_TABLE_CONFIG, DYNAMIC_PAGE_AI_CAPABILITIES, DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK, DYNAMIC_PAGE_CONFIG_EDITOR, DYNAMIC_PAGE_SHELL_EDITOR, DefaultLoadingRenderer, DeferredAsyncConfigStorage, DynamicFormService, DynamicGridPageComponent, DynamicWidgetLoaderDirective, DynamicWidgetPageComponent, EDITORIAL_ALLOWED_CONTENT_FORMATS, EDITORIAL_COMPLIANCE_PRESETS, EDITORIAL_EXTERNAL_LINK_REL, EDITORIAL_FORM_TEMPLATE_CATALOG, EDITORIAL_HTML_ENABLED, EDITORIAL_MARKDOWN_IMAGES_ENABLED, EDITORIAL_SOLUTION_CATALOG, EDITORIAL_SOLUTION_PRESETS, EDITORIAL_THEME_PRESETS, EDITORIAL_WIDGET_CONVENTION_INPUTS, EDITORIAL_WIDGET_TAG, EMPLOYEE_ONBOARDING_EDITORIAL_SOLUTION, EMPLOYEE_ONBOARDING_EDITORIAL_TEMPLATE, EMPLOYEE_ONBOARDING_GUIDED_EDITORIAL_SOLUTION, EMPLOYEE_ONBOARDING_GUIDED_EDITORIAL_TEMPLATE, EVENT_REGISTRATION_EDITORIAL_SOLUTION, EVENT_REGISTRATION_EDITORIAL_TEMPLATE, EmptyStateCardComponent, ErrorMessageService, FIELD_METADATA_CAPABILITIES, FIELD_SELECTOR_REGISTRY_BASE, FIELD_SELECTOR_REGISTRY_DISABLE_DEFAULTS, FIELD_SELECTOR_REGISTRY_OVERRIDES, FORM_HOOKS, FORM_HOOKS_PRESETS, FORM_HOOKS_WHITELIST, FORM_HOOK_RESOLVERS, FieldControlType, FieldDataType, FieldSelectorRegistry, FormHooksRegistry, GLOBAL_ACTION_CATALOG$1 as GLOBAL_ACTION_CATALOG, GLOBAL_ACTION_HANDLERS, GLOBAL_ACTION_CATALOG as GLOBAL_ACTION_SPEC_CATALOG, GLOBAL_ACTION_UI_SCHEMAS, GLOBAL_ANALYTICS_SERVICE, GLOBAL_API_CLIENT, GLOBAL_CONFIG, GLOBAL_DIALOG_SERVICE, GLOBAL_ROUTE_GUARD_RESOLVER, GLOBAL_SURFACE_SERVICE, GLOBAL_TOAST_SERVICE, GenericCrudService, GlobalActionService, GlobalConfigService, INLINE_FILTER_ALIAS_TOKENS, INLINE_FILTER_CONTROL_TYPES, INLINE_FILTER_CONTROL_TYPE_SET, INLINE_FILTER_CONTROL_TYPE_VALUES, INLINE_FILTER_TOKEN_TO_BASE_CONTROL_TYPE, INLINE_FILTER_TOKEN_TO_CONTROL_TYPE, IconPickerService, IconPosition, IconSize, LOGGER_LEVEL_BY_ENV, LOGGER_LEVEL_PRIORITY, LoadingOrchestrator, LocalConnectionStorage, LocalStorageAsyncAdapter, LocalStorageCacheAdapter, LocalStorageConfigService, LoggerService, LoggerThrottleTracker, LoggerWarnOnceTracker, MemoryCacheAdapter, NumericFormat, OVERLAY_DECIDER_DEBUG, OVERLAY_DECISION_MATRIX, ObservabilityDashboardService, OverlayDeciderService, PRAXIS_CORPORATE_SENSITIVE_KEYS, PRAXIS_DEFAULT_OBSERVABILITY_ALERT_RULES, PRAXIS_DYNAMIC_PAGE_COMPONENT_METADATA, PRAXIS_FOOTER_LINKS_METADATA, PRAXIS_GLOBAL_ACTION_CATALOG, PRAXIS_GLOBAL_CONFIG_BOOTSTRAP_OPTIONS, PRAXIS_GLOBAL_CONFIG_BOOTSTRAP_READY, PRAXIS_GLOBAL_CONFIG_TENANT_RESOLVER, PRAXIS_HERO_BANNER_METADATA, PRAXIS_I18N_CONFIG, PRAXIS_I18N_TRANSLATOR, PRAXIS_LEGAL_NOTICE_METADATA, PRAXIS_LOADING_CTX, PRAXIS_LOADING_RENDERER, PRAXIS_LOGGER_CONFIG, PRAXIS_LOGGER_SINKS, PRAXIS_OBSERVABILITY_DASHBOARD_OPTIONS, PRAXIS_RICH_TEXT_BLOCK_METADATA, PRAXIS_TELEMETRY_TRANSPORT, PRAXIS_USER_CONTEXT_SUMMARY_METADATA, PRIVACY_CONSENT_EDITORIAL_SOLUTION, PRIVACY_CONSENT_EDITORIAL_TEMPLATE, PraxisCore, PraxisFooterLinksComponent, PraxisGlobalErrorHandler, PraxisHeroBannerComponent, PraxisI18nService, PraxisIconDirective, PraxisIconPickerComponent, PraxisLegalNoticeComponent, PraxisLoadingInterceptor, PraxisRichTextBlockComponent, PraxisSurfaceHostComponent, PraxisUserContextSummaryComponent, RULE_PROPERTY_SCHEMA, RemoteConfigStorage, ResourceQuickConnectComponent, SCHEMA_VIEWER_CONTEXT, SETTINGS_PANEL_BRIDGE, SETTINGS_PANEL_DATA, STEPPER_CONFIG_EDITOR, SURFACE_OPEN_I18N_CONFIG, SURFACE_OPEN_I18N_NAMESPACE, SURFACE_OPEN_PRESETS, SchemaMetadataClient, SchemaNormalizerService, SchemaViewerComponent, SurfaceBindingRuntimeService, SurfaceOpenActionEditorComponent, TABLE_CONFIG_EDITOR, TableConfigService, TelemetryLoggerSink, TelemetryService, ValidationPattern, WidgetPageStateRuntimeService, WidgetShellComponent, applyLocalCustomizations$2 as applyLocalCustomizations, applyLocalCustomizations$1 as applyLocalFormCustomizations, buildAngularValidators, buildApiUrl, buildBaseColumnFromDef, buildBaseFormField, buildFormConfigFromEditorialTemplate, buildHeaders, buildPageKey, buildSchemaId, buildValidatorsFromValidatorOptions, cancelIfCpfInvalidHook, clampRange, cloneTableConfig, cnpjAlphaValidator, collapseWhitespace, composeHeadersWithVersion, conditionalAsyncValidator, convertFormLayoutToConfig, createCorporateLoggerConfig, createCorporateObservabilityOptions, createCpfCnpjValidator, createDefaultFormConfig, createDefaultTableConfig, createEmptyFormConfig, createPersistedPage, customAsyncValidatorFn, customValidatorFn, debounceAsyncValidator, deepMerge, ensureIds, ensureNoConflictsHookFactory, ensurePageIds, extractNormalizedError, fetchWithETag, fileTypeValidator, fillUndefined, generateId, getDefaultFormHints, getEditorialCompliancePresetById, getEditorialFormTemplateById, getEditorialFormTemplateCatalog, getEditorialSolutionById, getEditorialSolutionCatalog, getEditorialSolutionPresetById, getEditorialThemePresetById, getEssentialConfig, getFieldMetadataCapabilities, getGlobalActionCatalog, getGlobalActionUiSchema, getReferencedFieldMetadata, getTextTransformer, interpolatePraxisTranslation, isAllowedEditorialContentFormat, isAllowedEditorialHref, isCssTextTransform, isEditorialComponentMeta, isInlineFilterControlType, isRangeValidForFilter, isTableConfigV2, isValidFormConfig, isValidTableConfig, legacyCnpjValidator, legacyCpfValidator, logOnErrorHook, mapFieldDefinitionToMetadata, mapFieldDefinitionsToMetadata, matchFieldValidator, maxFileSizeValidator, mergeFieldMetadata, mergePraxisI18nConfigs, mergeTableConfigs, migrateFormLayoutRule, minWordsValidator, normalizeControlTypeKey, normalizeControlTypeToken, normalizeEditorialLink, normalizeEnd, normalizeFieldConstraints, normalizeFormConfig, normalizeFormMetadata, normalizePath, normalizePraxisDataQueryContext, normalizeStart, normalizeUnknownError, notifySuccessHook, parseJsonResponseOrEmpty, praxisLoadingInterceptorFn, prefillFromContextHook, provideDefaultFormHooks, provideFieldSelectorRegistryBase, provideFieldSelectorRegistryOverride, provideFieldSelectorRegistryRuntime, provideFormHookPresets, provideFormHooks, provideGlobalActionCatalog, provideGlobalActionHandler, provideGlobalConfig, provideGlobalConfigReady, provideGlobalConfigSeed, provideGlobalConfigTenant, provideHookResolvers, provideHookWhitelist, provideOverlayDecisionMatrix, providePraxisAnalyticsGlobalActions, providePraxisDynamicPageMetadata, providePraxisFooterLinksMetadata, providePraxisGlobalActionCatalog, providePraxisGlobalActions, providePraxisGlobalConfigBootstrap, providePraxisHeroBannerMetadata, providePraxisHttpLoading, providePraxisI18n, providePraxisI18nConfig, providePraxisI18nTranslator, providePraxisLegalNoticeMetadata, providePraxisLoadingDefaults, providePraxisLogging, providePraxisRichTextBlockMetadata, providePraxisToastGlobalActions, providePraxisUserContextSummaryMetadata, provideRemoteGlobalConfig, reconcileFilterConfig, reconcileFormConfig, reconcileTableConfig, removeDiacritics, reportTelemetryHookFactory, requiredCheckedValidator, resolveBuiltinPresets, resolveControlTypeAlias, resolveDefaultValuePresentationFormat, resolveHidden, resolveInlineFilterControlType, resolveInlineFilterControlTypeToBaseControlType, resolveLoggerConfig, resolveObservabilityOptions, resolveOffset, resolveOrder, resolvePraxisFilterCriteria, resolveSpan, resolveValuePresentation, resolveValuePresentationLocale, slugify, stripMasksHook, supportsImplicitValuePresentation, syncWithServerMetadata, toCamel, toCapitalize, toKebab, toPascal, toSentenceCase, toSnake, toTitleCase, trim, uniqueAsyncValidator, urlValidator, withMessage, withPraxisHttpLoading };
18133
20983
  //# sourceMappingURL=praxisui-core.mjs.map