@praxisui/list 9.0.36 → 9.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ai/component-registry.json +77 -2
- package/fesm2022/praxisui-list.mjs +174 -35
- package/package.json +6 -6
- package/src/lib/praxis-list.json-api.md +27 -21
- package/types/praxisui-list.d.ts +16 -5
|
@@ -3,28 +3,32 @@ import { inject, Injectable, Input, ChangeDetectionStrategy, Component, LOCALE_I
|
|
|
3
3
|
import { ActivatedRoute } from '@angular/router';
|
|
4
4
|
import * as i1 from '@angular/common';
|
|
5
5
|
import { formatDate, CommonModule } from '@angular/common';
|
|
6
|
-
import * as
|
|
6
|
+
import * as i2 from '@angular/material/list';
|
|
7
7
|
import { MatListModule } from '@angular/material/list';
|
|
8
|
+
import * as i3$3 from '@angular/material/checkbox';
|
|
9
|
+
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
10
|
+
import * as i4$2 from '@angular/material/radio';
|
|
11
|
+
import { MatRadioModule } from '@angular/material/radio';
|
|
8
12
|
import * as i4 from '@angular/material/icon';
|
|
9
13
|
import { MatIconModule } from '@angular/material/icon';
|
|
10
14
|
import { resolveValuePresentation, LoggerService, GenericCrudService, toTitleCase, PraxisIconDirective, providePraxisI18n, PraxisI18nService, PraxisIconButtonComponent, validateGlobalActionRefs, IconPickerService, GLOBAL_ACTION_CATALOG, PRAXIS_GLOBAL_ACTION_CATALOG, getGlobalActionCatalog, SURFACE_OPEN_I18N_NAMESPACE, getGlobalActionUiSchema, SurfaceOpenActionEditorComponent, PRAXIS_I18N_CONFIG, providePraxisI18nConfig, SURFACE_OPEN_I18N_CONFIG, deepMerge, ASYNC_CONFIG_STORAGE, ComponentKeyService, PraxisJsonLogicService, PraxisCollectionExportService, GlobalActionService, GLOBAL_DIALOG_SERVICE, ResourceRecordOpenError, ResourceRecordOpenService, assertPraxisCollectionExportArtifact, normalizePraxisDataQueryContext, ComponentMetadataRegistry, API_URL, LocalStorageAsyncAdapter, LocalStorageConfigService } from '@praxisui/core';
|
|
11
15
|
import * as i5 from '@angular/material/chips';
|
|
12
16
|
import { MatChipsModule } from '@angular/material/chips';
|
|
13
17
|
import { MatDividerModule } from '@angular/material/divider';
|
|
14
|
-
import * as
|
|
18
|
+
import * as i7 from '@angular/material/button';
|
|
15
19
|
import { MatButtonModule } from '@angular/material/button';
|
|
16
20
|
import * as i5$1 from '@angular/material/menu';
|
|
17
21
|
import { MatMenuModule } from '@angular/material/menu';
|
|
18
|
-
import * as
|
|
22
|
+
import * as i9 from '@angular/material/progress-spinner';
|
|
19
23
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
20
|
-
import * as i2 from '@angular/material/form-field';
|
|
24
|
+
import * as i2$1 from '@angular/material/form-field';
|
|
21
25
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
22
|
-
import * as
|
|
26
|
+
import * as i11$1 from '@angular/material/paginator';
|
|
23
27
|
import { MatPaginatorIntl, MatPaginatorModule } from '@angular/material/paginator';
|
|
24
28
|
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
|
25
29
|
import * as i4$1 from '@angular/material/select';
|
|
26
30
|
import { MatSelectModule } from '@angular/material/select';
|
|
27
|
-
import * as i3
|
|
31
|
+
import * as i3 from '@angular/material/input';
|
|
28
32
|
import { MatInputModule } from '@angular/material/input';
|
|
29
33
|
import * as i12 from '@angular/material/tooltip';
|
|
30
34
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
@@ -34,7 +38,7 @@ import { BehaviorSubject, combineLatest, of, Subject, debounceTime, takeUntil, f
|
|
|
34
38
|
import { auditTime, map, distinctUntilChanged, switchMap, catchError, finalize, shareReplay, debounceTime as debounceTime$1, tap, take, takeUntil as takeUntil$1 } from 'rxjs/operators';
|
|
35
39
|
import { PraxisRichContent } from '@praxisui/rich-content';
|
|
36
40
|
import { SETTINGS_PANEL_DATA, SettingsPanelService } from '@praxisui/settings-panel';
|
|
37
|
-
import * as i3$
|
|
41
|
+
import * as i3$2 from '@angular/material/tabs';
|
|
38
42
|
import { MatTabsModule } from '@angular/material/tabs';
|
|
39
43
|
import * as i8 from '@angular/material/slide-toggle';
|
|
40
44
|
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|
@@ -45,7 +49,7 @@ import { MatExpansionModule } from '@angular/material/expansion';
|
|
|
45
49
|
import { PdxColorPickerComponent } from '@praxisui/dynamic-fields';
|
|
46
50
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
47
51
|
import { produce, setAutoFreeze } from 'immer';
|
|
48
|
-
import * as i3$
|
|
52
|
+
import * as i3$1 from '@angular/material/card';
|
|
49
53
|
import { MatCardModule } from '@angular/material/card';
|
|
50
54
|
import { BaseAiAdapter, withAuthoringScopePolicy, shouldRoutePromptToGovernedDecision, createPraxisAssistantViewportLayout, AiBackendApiService, PraxisAssistantSessionRegistryService, PraxisAssistantTurnOrchestratorService, PraxisAiAssistantShellComponent } from '@praxisui/ai';
|
|
51
55
|
|
|
@@ -1747,7 +1751,7 @@ class PraxisListSkinPreviewComponent {
|
|
|
1747
1751
|
// trackBy for preview lists
|
|
1748
1752
|
trackByIndex = (i) => i;
|
|
1749
1753
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisListSkinPreviewComponent, deps: [{ token: ListSkinService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1750
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisListSkinPreviewComponent, isStandalone: true, selector: "praxis-list-skin-preview", inputs: { config: "config", items: "items", theme: "theme" }, ngImport: i0, template: "<div class=\"preview-root\" [ngClass]=\"skinClasses\" [attr.data-skin-scope]=\"skinScopeId\">\n @if (inlineCss) { <style [attr.nonce]=\"cspNonce || null\" [textContent]=\"inlineCss\"></style> }\n\n @if (isList()) {\n <mat-list>\n @for (item of items; track trackByIndex($index)) {\n <mat-list-item>\n <div class=\"list-item-content\">\n @if (leading(item); as lead) {\n @if (previewRichContentNodes(lead, { imageAlt: config.templating?.leading?.imageAlt }); as leadNodes) {\n <praxis-rich-content [nodes]=\"leadNodes\"></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case ('icon') { <mat-icon [praxisIcon]=\"lead.value\" [ngClass]=\"lead.class\" [color]=\"isThemeColor(lead.color) ? lead.color : undefined\" [style.cssText]=\"(lead.style ? lead.style + ';' : '') + iconStyle(lead.color)\"></mat-icon> }\n @case ('image') {\n <div class=\"lead-image\" [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">\n <img [src]=\"lead.value\" [alt]=\"config.templating?.leading?.imageAlt || ''\" />\n @if (lead.badge?.value) { <mat-chip class=\"lead-badge\"\n [color]=\"isThemeColor(lead.badge?.color) ? lead.badge?.color : undefined\"\n [ngClass]=\"(((lead.badge?.variant || 'filled') === 'outlined') ? 'chip-outlined' : '')\"\n [style.cssText]=\"chipStyle(lead.badge?.color, lead.badge?.variant)\">{{ lead.badge?.value }}</mat-chip> }\n </div>\n }\n @case ('text') { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</span> }\n @case ('chip') { <mat-chip [color]=\"isThemeColor(lead.color) ? lead.color : undefined\" [ngClass]=\"((lead.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(lead.color, lead.variant)\">{{ lead.value }}</mat-chip> }\n @case ('rating') { @for (_ of ratingRange(lead); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, lead.value)\" [color]=\"ratingThemeColor(lead)\" [style.cssText]=\"ratingIconStyle(lead)\"></mat-icon> } }\n @case ('html') { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\" [innerHTML]=\"lead.value\"></span> }\n @default { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</span> }\n }\n }\n }\n <div class=\"list-item-text\">\n <div class=\"primary\" [ngClass]=\"primary(item)?.class\" [style.cssText]=\"primary(item)?.style\">\n @if (primary(item)?.type === 'html') {\n <span [innerHTML]=\"primary(item)?.value\"></span>\n } @else if (previewRichContentNodes(primary(item)); as primaryNodes) {\n <praxis-rich-content [nodes]=\"primaryNodes\"></praxis-rich-content>\n } @else {\n {{ primary(item)?.value }}\n }\n </div>\n @if ((config.layout?.lines || 1) > 1) {\n <div class=\"secondary\" [ngClass]=\"secondary(item)?.class\" [style.cssText]=\"secondary(item)?.style\">\n @if (secondary(item)?.type === 'html') {\n <span [innerHTML]=\"secondary(item)?.value\"></span>\n } @else if (previewRichContentNodes(secondary(item)); as secondaryNodes) {\n <praxis-rich-content [nodes]=\"secondaryNodes\"></praxis-rich-content>\n } @else {\n {{ secondary(item)?.value }}\n }\n </div>\n }\n @if (featuresVisible()) {\n @for (f of (config.templating?.features || []); track $index; let fi = $index) {\n <span class=\"feature\">\n @if (previewFeatureRichContentNodes(item, f); as featureNodes) {\n <praxis-rich-content [nodes]=\"featureNodes\"></praxis-rich-content>\n } @else {\n @if (f.icon && featuresMode() !== 'labels-only') { <mat-icon [praxisIcon]=\"f.icon\"></mat-icon> }\n @if (featuresMode() !== 'icons-only') { <span [ngClass]=\"f.class\" [style.cssText]=\"f.style\">{{ featureLabel(item, f.expr) }}</span> }\n }\n </span>\n }\n }\n </div>\n @if (meta(item); as m) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @if (metaPrefixIcon(); as mpi) { <mat-icon [praxisIcon]=\"mpi\"></mat-icon> }\n @if (previewRichContentNodes(m, { slot: 'meta' }); as metaNodes) {\n <praxis-rich-content [nodes]=\"metaNodes\"></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case ('chip') { <mat-chip [color]=\"isThemeColor(m.color) ? m.color : undefined\" [ngClass]=\"((m.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(m.color, m.variant)\">{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of ratingRange(m); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, m.value)\" [color]=\"ratingThemeColor(m)\" [style.cssText]=\"ratingIconStyle(m)\"></mat-icon> } }\n @case ('icon') { <mat-icon [praxisIcon]=\"m.value\" [color]=\"isThemeColor(m.color) ? m.color : undefined\" [style.cssText]=\"iconStyle(m.color)\"></mat-icon> }\n @case ('image') { <span class=\"inline-image\"><img [src]=\"m.value\" [alt]=\"m.imageAlt || ''\" /></span> }\n @case ('html') { <span [innerHTML]=\"m.value\"></span> }\n @default { <span>{{ m.value }}</span> }\n }\n }\n </div>\n }\n @if (trailing(item); as tr) {\n <div class=\"trailing\" [ngClass]=\"tr.class\" [style.cssText]=\"tr.style\">\n @if (previewRichContentNodes(tr); as trailingNodes) {\n <praxis-rich-content [nodes]=\"trailingNodes\"></praxis-rich-content>\n } @else {\n @switch (tr.type) {\n @case ('chip') { <mat-chip [color]=\"isThemeColor(tr.color) ? tr.color : undefined\" [ngClass]=\"((tr.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(tr.color, tr.variant)\">{{ tr.value }}</mat-chip> }\n @case ('icon') { <mat-icon [praxisIcon]=\"tr.value\" [color]=\"isThemeColor(tr.color) ? tr.color : undefined\" [style.cssText]=\"iconStyle(tr.color)\"></mat-icon> }\n @case ('image') { <span class=\"inline-image\"><img [src]=\"tr.value\" [alt]=\"tr.imageAlt || ''\" /></span> }\n @case ('rating') { @for (_ of ratingRange(tr); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, tr.value)\" [color]=\"ratingThemeColor(tr)\" [style.cssText]=\"ratingIconStyle(tr)\"></mat-icon> } }\n @case ('html') { <span [innerHTML]=\"tr.value\"></span> }\n @default { <span>{{ tr.value }}</span> }\n }\n }\n </div>\n }\n </div>\n </mat-list-item>\n }\n </mat-list>\n } @else if (isTiles()) {\n <div class=\"tiles-grid\">\n @for (it of items; track trackByIndex($index)) {\n <div class=\"item-tile\">\n <div class=\"tile-media\">\n @if (leading(it); as lead) {\n @if (previewRichContentNodes(lead, { imageAlt: config.templating?.leading?.imageAlt }); as leadNodes) {\n <praxis-rich-content [nodes]=\"leadNodes\"></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case ('image') {\n <img [src]=\"lead.value\" [alt]=\"config.templating?.leading?.imageAlt || ''\" />\n }\n @case ('icon') { <mat-icon [praxisIcon]=\"lead.value\" [ngClass]=\"lead.class\" [color]=\"isThemeColor(lead.color) ? lead.color : undefined\" [style.cssText]=\"(lead.style ? lead.style + ';' : '') + iconStyle(lead.color)\"></mat-icon> }\n @case ('text') { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</span> }\n @case ('chip') { <mat-chip [color]=\"isThemeColor(lead.color) ? lead.color : undefined\" [ngClass]=\"((lead.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(lead.color, lead.variant)\">{{ lead.value }}</mat-chip> }\n @case ('rating') { @for (_ of ratingRange(lead); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, lead.value)\" [color]=\"ratingThemeColor(lead)\" [style.cssText]=\"ratingIconStyle(lead)\"></mat-icon> } }\n @case ('html') { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\" [innerHTML]=\"lead.value\"></span> }\n @default { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</span> }\n }\n }\n }\n </div>\n @if (trailing(it); as tr) {\n @if (statusPosition() === 'top-right' && (tr?.type === 'chip' || tr?.type === 'icon')) {\n <div class=\"tile-status\">\n @if (previewRichContentNodes(tr); as trailingNodes) {\n <praxis-rich-content [nodes]=\"trailingNodes\"></praxis-rich-content>\n } @else {\n @if (tr?.type === 'chip') { <mat-chip [color]=\"isThemeColor(tr.color) ? tr.color : undefined\" [ngClass]=\"((tr.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(tr.color, tr.variant)\">{{ tr.value }}</mat-chip> }\n @if (tr?.type === 'icon') { <mat-icon [praxisIcon]=\"tr.value\" [color]=\"isThemeColor(tr.color) ? tr.color : undefined\" [style.cssText]=\"iconStyle(tr.color)\"></mat-icon> }\n }\n </div>\n }\n }\n <div class=\"tile-body\">\n <div class=\"primary\" [ngClass]=\"primary(it)?.class\" [style.cssText]=\"primary(it)?.style\">\n @if (primary(it)?.type === 'html') {\n <span [innerHTML]=\"primary(it)?.value\"></span>\n } @else if (previewRichContentNodes(primary(it)); as primaryNodes) {\n <praxis-rich-content [nodes]=\"primaryNodes\"></praxis-rich-content>\n } @else {\n {{ primary(it)?.value }}\n }\n </div>\n @if ((config.layout?.lines || 1) > 1) {\n <div class=\"secondary\" [ngClass]=\"secondary(it)?.class\" [style.cssText]=\"secondary(it)?.style\">\n @if (secondary(it)?.type === 'html') {\n <span [innerHTML]=\"secondary(it)?.value\"></span>\n } @else if (previewRichContentNodes(secondary(it)); as secondaryNodes) {\n <praxis-rich-content [nodes]=\"secondaryNodes\"></praxis-rich-content>\n } @else {\n {{ secondary(it)?.value }}\n }\n </div>\n }\n @if (meta(it); as m) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @if (metaPrefixIcon(); as mpi) { <mat-icon [praxisIcon]=\"mpi\"></mat-icon> }\n @if (previewRichContentNodes(m, { slot: 'meta' }); as metaNodes) {\n <praxis-rich-content [nodes]=\"metaNodes\"></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case ('chip') { <mat-chip [color]=\"isThemeColor(m.color) ? m.color : undefined\" [ngClass]=\"((m.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(m.color, m.variant)\">{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of ratingRange(m); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, m.value)\" [color]=\"ratingThemeColor(m)\" [style.cssText]=\"ratingIconStyle(m)\"></mat-icon> } }\n @case ('icon') { <mat-icon [praxisIcon]=\"m.value\" [color]=\"isThemeColor(m.color) ? m.color : undefined\" [style.cssText]=\"iconStyle(m.color)\"></mat-icon> }\n @case ('image') { <span class=\"inline-image\"><img [src]=\"m.value\" [alt]=\"m.imageAlt || ''\" /></span> }\n @case ('html') { <span [innerHTML]=\"m.value\"></span> }\n @default { <span>{{ m.value }}</span> }\n }\n }\n </div>\n }\n @if (featuresVisible()) {\n <div class=\"features\">\n @for (f of (config.templating?.features || []); track $index; let fi = $index) {\n <span class=\"feature\">\n @if (previewFeatureRichContentNodes(it, f); as featureNodes) {\n <praxis-rich-content [nodes]=\"featureNodes\"></praxis-rich-content>\n } @else {\n @if (f.icon && featuresMode() !== 'labels-only') { <mat-icon [praxisIcon]=\"f.icon\"></mat-icon> }\n @if (featuresMode() !== 'icons-only') { <span [ngClass]=\"f.class\" [style.cssText]=\"f.style\">{{ featureLabel(it, f.expr) }}</span> }\n }\n </span>\n }\n </div>\n }\n @if (trailing(it); as tr2) {\n @if (!(statusPosition() === 'top-right' && (tr2?.type === 'chip' || tr2?.type === 'icon'))) {\n <div class=\"meta\" [ngClass]=\"tr2.class\" [style.cssText]=\"tr2.style\">\n @if (previewRichContentNodes(tr2); as trailingNodes) {\n <praxis-rich-content [nodes]=\"trailingNodes\"></praxis-rich-content>\n } @else {\n @switch (tr2.type) {\n @case ('chip') { <mat-chip [color]=\"isThemeColor(tr2.color) ? tr2.color : undefined\" [ngClass]=\"((tr2.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(tr2.color, tr2.variant)\">{{ tr2.value }}</mat-chip> }\n @case ('rating') { @for (_ of ratingRange(tr2); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, tr2.value)\" [color]=\"ratingThemeColor(tr2)\" [style.cssText]=\"ratingIconStyle(tr2)\"></mat-icon> } }\n @case ('image') { <span class=\"inline-image\"><img [src]=\"tr2.value\" [alt]=\"tr2.imageAlt || ''\" /></span> }\n @case ('html') { <span [innerHTML]=\"tr2.value\"></span> }\n @default { <span>{{ tr2.value }}</span> }\n }\n }\n </div>\n }\n }\n </div>\n </div>\n }\n </div>\n } @else {\n <div class=\"cards-grid\">\n @for (it of items; track trackByIndex($index)) {\n <div class=\"item-card\">\n <div class=\"list-item-content\">\n @if (leading(it); as lead) {\n @if (previewRichContentNodes(lead, { imageAlt: config.templating?.leading?.imageAlt }); as leadNodes) {\n <praxis-rich-content [nodes]=\"leadNodes\"></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case ('icon') { <mat-icon [praxisIcon]=\"lead.value\" [ngClass]=\"lead.class\" [color]=\"isThemeColor(lead.color) ? lead.color : undefined\" [style.cssText]=\"(lead.style ? lead.style + ';' : '') + iconStyle(lead.color)\"></mat-icon> }\n @case ('image') {\n <div class=\"lead-image\" [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">\n <img [src]=\"lead.value\" [alt]=\"config.templating?.leading?.imageAlt || ''\" />\n @if (lead.badge?.value) { <mat-chip class=\"lead-badge\"\n [color]=\"isThemeColor(lead.badge?.color) ? lead.badge?.color : undefined\"\n [ngClass]=\"(((lead.badge?.variant || 'filled') === 'outlined') ? 'chip-outlined' : '')\"\n [style.cssText]=\"chipStyle(lead.badge?.color, lead.badge?.variant)\">{{ lead.badge?.value }}</mat-chip> }\n </div>\n }\n @case ('text') { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</span> }\n @case ('chip') { <mat-chip [color]=\"isThemeColor(lead.color) ? lead.color : undefined\" [ngClass]=\"((lead.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(lead.color, lead.variant)\">{{ lead.value }}</mat-chip> }\n @case ('rating') { @for (_ of ratingRange(lead); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, lead.value)\" [color]=\"ratingThemeColor(lead)\" [style.cssText]=\"ratingIconStyle(lead)\"></mat-icon> } }\n @case ('html') { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\" [innerHTML]=\"lead.value\"></span> }\n @default { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</span> }\n }\n }\n }\n <div class=\"list-item-text\">\n <div class=\"primary\" [ngClass]=\"primary(it)?.class\" [style.cssText]=\"primary(it)?.style\">\n @if (primary(it)?.type === 'html') {\n <span [innerHTML]=\"primary(it)?.value\"></span>\n } @else if (previewRichContentNodes(primary(it)); as primaryNodes) {\n <praxis-rich-content [nodes]=\"primaryNodes\"></praxis-rich-content>\n } @else {\n {{ primary(it)?.value }}\n }\n </div>\n @if ((config.layout?.lines || 1) > 1) {\n <div class=\"secondary\" [ngClass]=\"secondary(it)?.class\" [style.cssText]=\"secondary(it)?.style\">\n @if (secondary(it)?.type === 'html') {\n <span [innerHTML]=\"secondary(it)?.value\"></span>\n } @else if (previewRichContentNodes(secondary(it)); as secondaryNodes) {\n <praxis-rich-content [nodes]=\"secondaryNodes\"></praxis-rich-content>\n } @else {\n {{ secondary(it)?.value }}\n }\n </div>\n }\n @if (featuresVisible()) {\n @for (f of (config.templating?.features || []); track $index; let fi = $index) {\n <span class=\"feature\">\n @if (previewFeatureRichContentNodes(it, f); as featureNodes) {\n <praxis-rich-content [nodes]=\"featureNodes\"></praxis-rich-content>\n } @else {\n @if (f.icon && featuresMode() !== 'labels-only') { <mat-icon [praxisIcon]=\"f.icon\"></mat-icon> }\n @if (featuresMode() !== 'icons-only') { <span [ngClass]=\"f.class\" [style.cssText]=\"f.style\">{{ featureLabel(it, f.expr) }}</span> }\n }\n </span>\n }\n }\n </div>\n @if (meta(it); as m) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @if (metaPrefixIcon(); as mpi) { <mat-icon [praxisIcon]=\"mpi\"></mat-icon> }\n @if (previewRichContentNodes(m, { slot: 'meta' }); as metaNodes) {\n <praxis-rich-content [nodes]=\"metaNodes\"></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case ('chip') { <mat-chip [color]=\"isThemeColor(m.color) ? m.color : undefined\" [ngClass]=\"((m.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(m.color, m.variant)\">{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of ratingRange(m); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, m.value)\" [color]=\"ratingThemeColor(m)\" [style.cssText]=\"ratingIconStyle(m)\"></mat-icon> } }\n @case ('icon') { <mat-icon [praxisIcon]=\"m.value\" [color]=\"isThemeColor(m.color) ? m.color : undefined\" [style.cssText]=\"iconStyle(m.color)\"></mat-icon> }\n @case ('image') { <span class=\"inline-image\"><img [src]=\"m.value\" [alt]=\"m.imageAlt || ''\" /></span> }\n @case ('html') { <span [innerHTML]=\"m.value\"></span> }\n @default { <span>{{ m.value }}</span> }\n }\n }\n </div>\n }\n @if (trailing(it); as tr) {\n <div class=\"trailing\" [ngClass]=\"tr.class\" [style.cssText]=\"tr.style\">\n @if (previewRichContentNodes(tr); as trailingNodes) {\n <praxis-rich-content [nodes]=\"trailingNodes\"></praxis-rich-content>\n } @else {\n @switch (tr.type) {\n @case ('chip') { <mat-chip [color]=\"isThemeColor(tr.color) ? tr.color : undefined\" [ngClass]=\"((tr.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(tr.color, tr.variant)\">{{ tr.value }}</mat-chip> }\n @case ('icon') { <mat-icon [praxisIcon]=\"tr.value\" [color]=\"isThemeColor(tr.color) ? tr.color : undefined\" [style.cssText]=\"iconStyle(tr.color)\"></mat-icon> }\n @case ('image') { <span class=\"inline-image\"><img [src]=\"tr.value\" [alt]=\"tr.imageAlt || ''\" /></span> }\n @case ('rating') { @for (_ of ratingRange(tr); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, tr.value)\" [color]=\"ratingThemeColor(tr)\" [style.cssText]=\"ratingIconStyle(tr)\"></mat-icon> } }\n @case ('html') { <span [innerHTML]=\"tr.value\"></span> }\n @default { <span>{{ tr.value }}</span> }\n }\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n }\n</div>\n", styles: [".preview-root{--p-list-radius: var(--md-sys-shape-corner-medium, 16px);--p-list-shadow: var(--md-sys-elevation-level2);--p-list-border: 1px solid var(--md-sys-color-outline-variant);--p-list-blur: 10px;--p-list-foreground: var(--md-sys-color-on-surface);--p-list-foreground-muted: var(--md-sys-color-on-surface-variant);--p-list-grad-foreground: var( --md-sys-color-on-primary-container, var(--md-sys-color-on-surface) );--p-list-grad-foreground-muted: color-mix( in srgb, var(--p-list-grad-foreground) 76%, transparent );--p-list-surface: var(--md-sys-color-surface-container);--p-list-accent: var(--md-sys-color-primary);--p-list-item-border: 1px solid var(--md-sys-color-outline-variant);--p-list-grad-from: var(--md-sys-color-primary-container);--p-list-grad-to: var(--md-sys-color-tertiary-container);--p-list-grad-angle: 135deg;--p-list-tile-minW: 240px;--p-list-tile-gap: 16px;--p-list-tile-padding: 16px;--p-list-tile-radius: 16px;--p-list-tile-media-radius: 12px;--p-list-tile-media-ratio: 1 / 1;padding:12px;border-radius:12px;overflow:hidden}.preview-light{background:var(--md-sys-color-surface-container-low, var(--md-sys-color-surface))}.preview-dark{background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface))}.preview-grid{background-color:var(--md-sys-color-surface-variant, var(--md-sys-color-surface));background-image:linear-gradient(var(--md-sys-color-outline-variant, var(--md-sys-color-outline)) 1px,transparent 1px),linear-gradient(90deg,var(--md-sys-color-outline-variant, var(--md-sys-color-outline)) 1px,transparent 1px);background-size:16px 16px}.skin-elevated,.skin-outline,.skin-flat,.skin-neumorphism{--p-list-item-surface: var(--mdc-elevated-card-container-color, var(--p-list-surface));--p-list-item-border: var(--p-list-border)}.skin-elevated .item-card,.skin-outline .item-card,.skin-flat .item-card,.skin-neumorphism .item-card{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:var(--p-list-radius);box-shadow:var(--p-list-shadow);border:var(--p-list-border)}.skin-elevated .item-tile,.skin-outline .item-tile,.skin-flat .item-tile,.skin-neumorphism .item-tile{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));box-shadow:var(--p-list-shadow);border:var(--p-list-border)}.skin-elevated .mat-mdc-list-item .list-item-content,.skin-outline .mat-mdc-list-item .list-item-content,.skin-flat .mat-mdc-list-item .list-item-content,.skin-neumorphism .mat-mdc-list-item .list-item-content{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * .75);box-shadow:var(--p-list-shadow);border:var(--p-list-item-border);color:var(--p-list-foreground)}.skin-outline{--p-list-shadow: none;--p-list-border: 1px solid var(--md-sys-color-outline)}.skin-flat{--p-list-shadow: none;--p-list-border: 1px solid var(--md-sys-color-outline-variant)}.skin-neumorphism{--p-list-shadow: var(--md-sys-elevation-level2);--p-list-border: 1px solid var(--md-sys-color-outline-variant);--p-list-radius: 1.25rem}.skin-pill-soft .item-card,.skin-pill-soft .item-tile{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:var(--md-sys-elevation-level1);border:var(--p-list-border)}.skin-pill-soft .mat-mdc-list-item .list-item-content{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:var(--md-sys-elevation-level1);border:var(--p-list-border);color:var(--p-list-foreground)}.lead-image{position:relative;width:96px;height:72px;border-radius:12px;overflow:hidden}.lead-image img{width:100%;height:100%;object-fit:cover;display:block}.lead-image .lead-badge{position:absolute;left:8px;bottom:8px}.features{display:flex;flex-wrap:wrap;gap:8px;margin-top:2px;color:var(--p-list-foreground-muted);min-width:0}.feature{display:inline-flex;align-items:center;gap:4px;min-width:0;max-width:100%;font-size:.75rem;line-height:1.25}.feature mat-icon{flex:0 0 auto;width:16px;height:16px;font-size:16px}.model-media .lead-image{width:136px;height:96px;border-radius:16px}.model-hotel .lead-image{width:160px;height:110px;border-radius:18px}.skin-gradient-tile{--p-list-foreground: var(--p-list-grad-foreground);--p-list-foreground-muted: var(--p-list-grad-foreground-muted)}.skin-gradient-tile .item-card,.skin-gradient-tile .item-tile{background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));border-radius:var(--p-list-radius);color:var(--p-list-foreground)}.skin-gradient-tile .tile-media{background:color-mix(in srgb,var(--md-sys-color-surface) 74%,transparent);border-color:color-mix(in srgb,var(--p-list-foreground) 22%,transparent)}.skin-gradient-tile .tile-media mat-icon,.skin-gradient-tile .tile-meta,.skin-gradient-tile .tile-trailing,.skin-gradient-tile .feature,.skin-gradient-tile .secondary,.skin-gradient-tile .meta{color:var(--p-list-foreground-muted)}.skin-gradient-tile .tile-status,.skin-gradient-tile [data-rich-node-type=badge] .prx-rich-badge{color:var(--md-sys-color-on-secondary-container, var(--p-list-foreground));background:var(--md-sys-color-secondary-container, rgba(255, 255, 255, .72))}.skin-gradient-tile .mat-mdc-list-item .list-item-content{background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));border-radius:var(--p-list-radius);color:var(--p-list-foreground)}.skin-glass .item-card,.skin-glass .item-tile{background:var(--md-sys-color-surface-container-low);border-radius:var(--p-list-radius);border:1px solid var(--md-sys-color-outline-variant);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur))}.skin-glass .mat-mdc-list-item .list-item-content{background:var(--md-sys-color-surface-container-low);border-radius:var(--p-list-radius);border:1px solid var(--md-sys-color-outline-variant);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur));color:var(--p-list-foreground)}.list-item-content{display:grid;grid-template-columns:auto minmax(0,1fr) auto auto;align-items:center;gap:12px;padding:12px 16px;min-width:0}.primary{font-weight:600;color:var(--p-list-foreground);min-width:0;overflow:hidden;text-overflow:ellipsis}.secondary{color:var(--p-list-foreground-muted);min-width:0;overflow:hidden}.meta{color:var(--p-list-foreground-muted);min-width:0}.trailing{color:var(--p-list-foreground-muted);text-align:end;min-width:0}.cards-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(var(--p-list-tile-minW),1fr));gap:var(--p-list-tile-gap)}.item-card{position:relative;display:grid;min-height:236px;padding:10px;overflow:hidden;color:var(--p-list-foreground)}.item-card .list-item-content{display:block;grid-template-columns:minmax(0,1fr);align-items:stretch;gap:8px;padding:0;min-width:0;overflow:hidden}.item-card .list-item-text{display:grid;gap:8px;min-width:0;width:100%}.item-card .lead-image{width:100%;height:auto;aspect-ratio:16/9;max-height:104px;border-radius:10px}.item-card .lead-image .lead-badge{max-width:calc(100% - 16px)}.item-card .primary{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-height:1.28;min-width:0;overflow-wrap:anywhere}.item-card .secondary{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;font-size:.78rem;line-height:1.3;color:color-mix(in srgb,var(--p-list-foreground) 72%,transparent);min-width:0;overflow-wrap:anywhere}.item-card .meta{display:inline-flex;align-items:center;gap:2px;max-width:100%;overflow:hidden;white-space:nowrap;font-size:.76rem;line-height:1.2;color:color-mix(in srgb,var(--p-list-foreground) 72%,transparent)}.item-card .meta mat-icon{flex:0 0 auto;width:16px;height:16px;font-size:16px}.item-card .trailing{justify-self:start;max-width:100%;text-align:start;color:inherit}.item-card .features{color:color-mix(in srgb,var(--p-list-foreground) 72%,transparent);min-width:0}.item-card .feature{min-width:0;max-width:100%;overflow-wrap:anywhere}.item-card .mat-mdc-chip{max-width:100%}.preview-root ::ng-deep praxis-rich-content .prx-rich-node[data-rich-node-type=badge]{background:transparent;color:inherit}.preview-root ::ng-deep praxis-rich-content .prx-rich-node[data-rich-node-type=badge] .prx-rich-badge{min-height:22px;padding:1px 9px;border:1px solid var(--p-list-chip-border-color, transparent);background:var(--p-list-chip-background, var(--md-sys-color-primary-container, #e8def8));color:var(--p-list-chip-color, var(--md-sys-color-on-primary-container, #21005d));line-height:18px}.item-card .mdc-evolution-chip__text-label,.item-card [data-rich-node-type=badge]{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tiles-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(var(--p-list-tile-minW),1fr));gap:var(--p-list-tile-gap)}.item-tile{padding:var(--p-list-tile-padding);border-radius:var(--p-list-tile-radius);border:var(--p-list-border);background:var(--p-list-surface);display:grid;gap:8px}.tile-media{width:100%;aspect-ratio:var(--p-list-tile-media-ratio);border-radius:var(--p-list-tile-media-radius);background:var(--md-sys-color-surface-container-low, var(--p-list-surface));display:grid;place-items:center;overflow:hidden}.tile-media img{width:100%;height:100%;object-fit:cover}.tile-body{display:grid;gap:4px}.chip-outlined.mat-mdc-chip{background:transparent!important;border:1px solid currentColor}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i3.MatList, selector: "mat-list", exportAs: ["matList"] }, { kind: "component", type: i3.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i5.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["role", "id", "aria-label", "aria-description", "value", "color", "removable", "highlighted", "disableRipple", "disabled"], outputs: ["removed", "destroyed"], exportAs: ["matChip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisRichContent, selector: "praxis-rich-content", inputs: ["document", "nodes", "context", "hostCapabilities", "layout", "rootClassName"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1754
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisListSkinPreviewComponent, isStandalone: true, selector: "praxis-list-skin-preview", inputs: { config: "config", items: "items", theme: "theme" }, ngImport: i0, template: "<div class=\"preview-root\" [ngClass]=\"skinClasses\" [attr.data-skin-scope]=\"skinScopeId\">\n @if (inlineCss) { <style [attr.nonce]=\"cspNonce || null\" [textContent]=\"inlineCss\"></style> }\n\n @if (isList()) {\n <mat-list>\n @for (item of items; track trackByIndex($index)) {\n <mat-list-item>\n <div class=\"list-item-content\">\n @if (leading(item); as lead) {\n @if (previewRichContentNodes(lead, { imageAlt: config.templating?.leading?.imageAlt }); as leadNodes) {\n <praxis-rich-content [nodes]=\"leadNodes\"></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case ('icon') { <mat-icon [praxisIcon]=\"lead.value\" [ngClass]=\"lead.class\" [color]=\"isThemeColor(lead.color) ? lead.color : undefined\" [style.cssText]=\"(lead.style ? lead.style + ';' : '') + iconStyle(lead.color)\"></mat-icon> }\n @case ('image') {\n <div class=\"lead-image\" [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">\n <img [src]=\"lead.value\" [alt]=\"config.templating?.leading?.imageAlt || ''\" />\n @if (lead.badge?.value) { <mat-chip class=\"lead-badge\"\n [color]=\"isThemeColor(lead.badge?.color) ? lead.badge?.color : undefined\"\n [ngClass]=\"(((lead.badge?.variant || 'filled') === 'outlined') ? 'chip-outlined' : '')\"\n [style.cssText]=\"chipStyle(lead.badge?.color, lead.badge?.variant)\">{{ lead.badge?.value }}</mat-chip> }\n </div>\n }\n @case ('text') { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</span> }\n @case ('chip') { <mat-chip [color]=\"isThemeColor(lead.color) ? lead.color : undefined\" [ngClass]=\"((lead.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(lead.color, lead.variant)\">{{ lead.value }}</mat-chip> }\n @case ('rating') { @for (_ of ratingRange(lead); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, lead.value)\" [color]=\"ratingThemeColor(lead)\" [style.cssText]=\"ratingIconStyle(lead)\"></mat-icon> } }\n @case ('html') { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\" [innerHTML]=\"lead.value\"></span> }\n @default { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</span> }\n }\n }\n }\n <div class=\"list-item-text\">\n <div class=\"primary\" [ngClass]=\"primary(item)?.class\" [style.cssText]=\"primary(item)?.style\">\n @if (primary(item)?.type === 'html') {\n <span [innerHTML]=\"primary(item)?.value\"></span>\n } @else if (previewRichContentNodes(primary(item)); as primaryNodes) {\n <praxis-rich-content [nodes]=\"primaryNodes\"></praxis-rich-content>\n } @else {\n {{ primary(item)?.value }}\n }\n </div>\n @if ((config.layout?.lines || 1) > 1) {\n <div class=\"secondary\" [ngClass]=\"secondary(item)?.class\" [style.cssText]=\"secondary(item)?.style\">\n @if (secondary(item)?.type === 'html') {\n <span [innerHTML]=\"secondary(item)?.value\"></span>\n } @else if (previewRichContentNodes(secondary(item)); as secondaryNodes) {\n <praxis-rich-content [nodes]=\"secondaryNodes\"></praxis-rich-content>\n } @else {\n {{ secondary(item)?.value }}\n }\n </div>\n }\n @if (featuresVisible()) {\n @for (f of (config.templating?.features || []); track $index; let fi = $index) {\n <span class=\"feature\">\n @if (previewFeatureRichContentNodes(item, f); as featureNodes) {\n <praxis-rich-content [nodes]=\"featureNodes\"></praxis-rich-content>\n } @else {\n @if (f.icon && featuresMode() !== 'labels-only') { <mat-icon [praxisIcon]=\"f.icon\"></mat-icon> }\n @if (featuresMode() !== 'icons-only') { <span [ngClass]=\"f.class\" [style.cssText]=\"f.style\">{{ featureLabel(item, f.expr) }}</span> }\n }\n </span>\n }\n }\n </div>\n @if (meta(item); as m) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @if (metaPrefixIcon(); as mpi) { <mat-icon [praxisIcon]=\"mpi\"></mat-icon> }\n @if (previewRichContentNodes(m, { slot: 'meta' }); as metaNodes) {\n <praxis-rich-content [nodes]=\"metaNodes\"></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case ('chip') { <mat-chip [color]=\"isThemeColor(m.color) ? m.color : undefined\" [ngClass]=\"((m.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(m.color, m.variant)\">{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of ratingRange(m); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, m.value)\" [color]=\"ratingThemeColor(m)\" [style.cssText]=\"ratingIconStyle(m)\"></mat-icon> } }\n @case ('icon') { <mat-icon [praxisIcon]=\"m.value\" [color]=\"isThemeColor(m.color) ? m.color : undefined\" [style.cssText]=\"iconStyle(m.color)\"></mat-icon> }\n @case ('image') { <span class=\"inline-image\"><img [src]=\"m.value\" [alt]=\"m.imageAlt || ''\" /></span> }\n @case ('html') { <span [innerHTML]=\"m.value\"></span> }\n @default { <span>{{ m.value }}</span> }\n }\n }\n </div>\n }\n @if (trailing(item); as tr) {\n <div class=\"trailing\" [ngClass]=\"tr.class\" [style.cssText]=\"tr.style\">\n @if (previewRichContentNodes(tr); as trailingNodes) {\n <praxis-rich-content [nodes]=\"trailingNodes\"></praxis-rich-content>\n } @else {\n @switch (tr.type) {\n @case ('chip') { <mat-chip [color]=\"isThemeColor(tr.color) ? tr.color : undefined\" [ngClass]=\"((tr.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(tr.color, tr.variant)\">{{ tr.value }}</mat-chip> }\n @case ('icon') { <mat-icon [praxisIcon]=\"tr.value\" [color]=\"isThemeColor(tr.color) ? tr.color : undefined\" [style.cssText]=\"iconStyle(tr.color)\"></mat-icon> }\n @case ('image') { <span class=\"inline-image\"><img [src]=\"tr.value\" [alt]=\"tr.imageAlt || ''\" /></span> }\n @case ('rating') { @for (_ of ratingRange(tr); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, tr.value)\" [color]=\"ratingThemeColor(tr)\" [style.cssText]=\"ratingIconStyle(tr)\"></mat-icon> } }\n @case ('html') { <span [innerHTML]=\"tr.value\"></span> }\n @default { <span>{{ tr.value }}</span> }\n }\n }\n </div>\n }\n </div>\n </mat-list-item>\n }\n </mat-list>\n } @else if (isTiles()) {\n <div class=\"tiles-grid\">\n @for (it of items; track trackByIndex($index)) {\n <div class=\"item-tile\">\n <div class=\"tile-media\">\n @if (leading(it); as lead) {\n @if (previewRichContentNodes(lead, { imageAlt: config.templating?.leading?.imageAlt }); as leadNodes) {\n <praxis-rich-content [nodes]=\"leadNodes\"></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case ('image') {\n <img [src]=\"lead.value\" [alt]=\"config.templating?.leading?.imageAlt || ''\" />\n }\n @case ('icon') { <mat-icon [praxisIcon]=\"lead.value\" [ngClass]=\"lead.class\" [color]=\"isThemeColor(lead.color) ? lead.color : undefined\" [style.cssText]=\"(lead.style ? lead.style + ';' : '') + iconStyle(lead.color)\"></mat-icon> }\n @case ('text') { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</span> }\n @case ('chip') { <mat-chip [color]=\"isThemeColor(lead.color) ? lead.color : undefined\" [ngClass]=\"((lead.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(lead.color, lead.variant)\">{{ lead.value }}</mat-chip> }\n @case ('rating') { @for (_ of ratingRange(lead); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, lead.value)\" [color]=\"ratingThemeColor(lead)\" [style.cssText]=\"ratingIconStyle(lead)\"></mat-icon> } }\n @case ('html') { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\" [innerHTML]=\"lead.value\"></span> }\n @default { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</span> }\n }\n }\n }\n </div>\n @if (trailing(it); as tr) {\n @if (statusPosition() === 'top-right' && (tr?.type === 'chip' || tr?.type === 'icon')) {\n <div class=\"tile-status\">\n @if (previewRichContentNodes(tr); as trailingNodes) {\n <praxis-rich-content [nodes]=\"trailingNodes\"></praxis-rich-content>\n } @else {\n @if (tr?.type === 'chip') { <mat-chip [color]=\"isThemeColor(tr.color) ? tr.color : undefined\" [ngClass]=\"((tr.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(tr.color, tr.variant)\">{{ tr.value }}</mat-chip> }\n @if (tr?.type === 'icon') { <mat-icon [praxisIcon]=\"tr.value\" [color]=\"isThemeColor(tr.color) ? tr.color : undefined\" [style.cssText]=\"iconStyle(tr.color)\"></mat-icon> }\n }\n </div>\n }\n }\n <div class=\"tile-body\">\n <div class=\"primary\" [ngClass]=\"primary(it)?.class\" [style.cssText]=\"primary(it)?.style\">\n @if (primary(it)?.type === 'html') {\n <span [innerHTML]=\"primary(it)?.value\"></span>\n } @else if (previewRichContentNodes(primary(it)); as primaryNodes) {\n <praxis-rich-content [nodes]=\"primaryNodes\"></praxis-rich-content>\n } @else {\n {{ primary(it)?.value }}\n }\n </div>\n @if ((config.layout?.lines || 1) > 1) {\n <div class=\"secondary\" [ngClass]=\"secondary(it)?.class\" [style.cssText]=\"secondary(it)?.style\">\n @if (secondary(it)?.type === 'html') {\n <span [innerHTML]=\"secondary(it)?.value\"></span>\n } @else if (previewRichContentNodes(secondary(it)); as secondaryNodes) {\n <praxis-rich-content [nodes]=\"secondaryNodes\"></praxis-rich-content>\n } @else {\n {{ secondary(it)?.value }}\n }\n </div>\n }\n @if (meta(it); as m) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @if (metaPrefixIcon(); as mpi) { <mat-icon [praxisIcon]=\"mpi\"></mat-icon> }\n @if (previewRichContentNodes(m, { slot: 'meta' }); as metaNodes) {\n <praxis-rich-content [nodes]=\"metaNodes\"></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case ('chip') { <mat-chip [color]=\"isThemeColor(m.color) ? m.color : undefined\" [ngClass]=\"((m.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(m.color, m.variant)\">{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of ratingRange(m); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, m.value)\" [color]=\"ratingThemeColor(m)\" [style.cssText]=\"ratingIconStyle(m)\"></mat-icon> } }\n @case ('icon') { <mat-icon [praxisIcon]=\"m.value\" [color]=\"isThemeColor(m.color) ? m.color : undefined\" [style.cssText]=\"iconStyle(m.color)\"></mat-icon> }\n @case ('image') { <span class=\"inline-image\"><img [src]=\"m.value\" [alt]=\"m.imageAlt || ''\" /></span> }\n @case ('html') { <span [innerHTML]=\"m.value\"></span> }\n @default { <span>{{ m.value }}</span> }\n }\n }\n </div>\n }\n @if (featuresVisible()) {\n <div class=\"features\">\n @for (f of (config.templating?.features || []); track $index; let fi = $index) {\n <span class=\"feature\">\n @if (previewFeatureRichContentNodes(it, f); as featureNodes) {\n <praxis-rich-content [nodes]=\"featureNodes\"></praxis-rich-content>\n } @else {\n @if (f.icon && featuresMode() !== 'labels-only') { <mat-icon [praxisIcon]=\"f.icon\"></mat-icon> }\n @if (featuresMode() !== 'icons-only') { <span [ngClass]=\"f.class\" [style.cssText]=\"f.style\">{{ featureLabel(it, f.expr) }}</span> }\n }\n </span>\n }\n </div>\n }\n @if (trailing(it); as tr2) {\n @if (!(statusPosition() === 'top-right' && (tr2?.type === 'chip' || tr2?.type === 'icon'))) {\n <div class=\"meta\" [ngClass]=\"tr2.class\" [style.cssText]=\"tr2.style\">\n @if (previewRichContentNodes(tr2); as trailingNodes) {\n <praxis-rich-content [nodes]=\"trailingNodes\"></praxis-rich-content>\n } @else {\n @switch (tr2.type) {\n @case ('chip') { <mat-chip [color]=\"isThemeColor(tr2.color) ? tr2.color : undefined\" [ngClass]=\"((tr2.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(tr2.color, tr2.variant)\">{{ tr2.value }}</mat-chip> }\n @case ('rating') { @for (_ of ratingRange(tr2); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, tr2.value)\" [color]=\"ratingThemeColor(tr2)\" [style.cssText]=\"ratingIconStyle(tr2)\"></mat-icon> } }\n @case ('image') { <span class=\"inline-image\"><img [src]=\"tr2.value\" [alt]=\"tr2.imageAlt || ''\" /></span> }\n @case ('html') { <span [innerHTML]=\"tr2.value\"></span> }\n @default { <span>{{ tr2.value }}</span> }\n }\n }\n </div>\n }\n }\n </div>\n </div>\n }\n </div>\n } @else {\n <div class=\"cards-grid\">\n @for (it of items; track trackByIndex($index)) {\n <div class=\"item-card\">\n <div class=\"list-item-content\">\n @if (leading(it); as lead) {\n @if (previewRichContentNodes(lead, { imageAlt: config.templating?.leading?.imageAlt }); as leadNodes) {\n <praxis-rich-content [nodes]=\"leadNodes\"></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case ('icon') { <mat-icon [praxisIcon]=\"lead.value\" [ngClass]=\"lead.class\" [color]=\"isThemeColor(lead.color) ? lead.color : undefined\" [style.cssText]=\"(lead.style ? lead.style + ';' : '') + iconStyle(lead.color)\"></mat-icon> }\n @case ('image') {\n <div class=\"lead-image\" [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">\n <img [src]=\"lead.value\" [alt]=\"config.templating?.leading?.imageAlt || ''\" />\n @if (lead.badge?.value) { <mat-chip class=\"lead-badge\"\n [color]=\"isThemeColor(lead.badge?.color) ? lead.badge?.color : undefined\"\n [ngClass]=\"(((lead.badge?.variant || 'filled') === 'outlined') ? 'chip-outlined' : '')\"\n [style.cssText]=\"chipStyle(lead.badge?.color, lead.badge?.variant)\">{{ lead.badge?.value }}</mat-chip> }\n </div>\n }\n @case ('text') { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</span> }\n @case ('chip') { <mat-chip [color]=\"isThemeColor(lead.color) ? lead.color : undefined\" [ngClass]=\"((lead.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(lead.color, lead.variant)\">{{ lead.value }}</mat-chip> }\n @case ('rating') { @for (_ of ratingRange(lead); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, lead.value)\" [color]=\"ratingThemeColor(lead)\" [style.cssText]=\"ratingIconStyle(lead)\"></mat-icon> } }\n @case ('html') { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\" [innerHTML]=\"lead.value\"></span> }\n @default { <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</span> }\n }\n }\n }\n <div class=\"list-item-text\">\n <div class=\"primary\" [ngClass]=\"primary(it)?.class\" [style.cssText]=\"primary(it)?.style\">\n @if (primary(it)?.type === 'html') {\n <span [innerHTML]=\"primary(it)?.value\"></span>\n } @else if (previewRichContentNodes(primary(it)); as primaryNodes) {\n <praxis-rich-content [nodes]=\"primaryNodes\"></praxis-rich-content>\n } @else {\n {{ primary(it)?.value }}\n }\n </div>\n @if ((config.layout?.lines || 1) > 1) {\n <div class=\"secondary\" [ngClass]=\"secondary(it)?.class\" [style.cssText]=\"secondary(it)?.style\">\n @if (secondary(it)?.type === 'html') {\n <span [innerHTML]=\"secondary(it)?.value\"></span>\n } @else if (previewRichContentNodes(secondary(it)); as secondaryNodes) {\n <praxis-rich-content [nodes]=\"secondaryNodes\"></praxis-rich-content>\n } @else {\n {{ secondary(it)?.value }}\n }\n </div>\n }\n @if (featuresVisible()) {\n @for (f of (config.templating?.features || []); track $index; let fi = $index) {\n <span class=\"feature\">\n @if (previewFeatureRichContentNodes(it, f); as featureNodes) {\n <praxis-rich-content [nodes]=\"featureNodes\"></praxis-rich-content>\n } @else {\n @if (f.icon && featuresMode() !== 'labels-only') { <mat-icon [praxisIcon]=\"f.icon\"></mat-icon> }\n @if (featuresMode() !== 'icons-only') { <span [ngClass]=\"f.class\" [style.cssText]=\"f.style\">{{ featureLabel(it, f.expr) }}</span> }\n }\n </span>\n }\n }\n </div>\n @if (meta(it); as m) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @if (metaPrefixIcon(); as mpi) { <mat-icon [praxisIcon]=\"mpi\"></mat-icon> }\n @if (previewRichContentNodes(m, { slot: 'meta' }); as metaNodes) {\n <praxis-rich-content [nodes]=\"metaNodes\"></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case ('chip') { <mat-chip [color]=\"isThemeColor(m.color) ? m.color : undefined\" [ngClass]=\"((m.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(m.color, m.variant)\">{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of ratingRange(m); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, m.value)\" [color]=\"ratingThemeColor(m)\" [style.cssText]=\"ratingIconStyle(m)\"></mat-icon> } }\n @case ('icon') { <mat-icon [praxisIcon]=\"m.value\" [color]=\"isThemeColor(m.color) ? m.color : undefined\" [style.cssText]=\"iconStyle(m.color)\"></mat-icon> }\n @case ('image') { <span class=\"inline-image\"><img [src]=\"m.value\" [alt]=\"m.imageAlt || ''\" /></span> }\n @case ('html') { <span [innerHTML]=\"m.value\"></span> }\n @default { <span>{{ m.value }}</span> }\n }\n }\n </div>\n }\n @if (trailing(it); as tr) {\n <div class=\"trailing\" [ngClass]=\"tr.class\" [style.cssText]=\"tr.style\">\n @if (previewRichContentNodes(tr); as trailingNodes) {\n <praxis-rich-content [nodes]=\"trailingNodes\"></praxis-rich-content>\n } @else {\n @switch (tr.type) {\n @case ('chip') { <mat-chip [color]=\"isThemeColor(tr.color) ? tr.color : undefined\" [ngClass]=\"((tr.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\" [style.cssText]=\"chipStyle(tr.color, tr.variant)\">{{ tr.value }}</mat-chip> }\n @case ('icon') { <mat-icon [praxisIcon]=\"tr.value\" [color]=\"isThemeColor(tr.color) ? tr.color : undefined\" [style.cssText]=\"iconStyle(tr.color)\"></mat-icon> }\n @case ('image') { <span class=\"inline-image\"><img [src]=\"tr.value\" [alt]=\"tr.imageAlt || ''\" /></span> }\n @case ('rating') { @for (_ of ratingRange(tr); track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, tr.value)\" [color]=\"ratingThemeColor(tr)\" [style.cssText]=\"ratingIconStyle(tr)\"></mat-icon> } }\n @case ('html') { <span [innerHTML]=\"tr.value\"></span> }\n @default { <span>{{ tr.value }}</span> }\n }\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n }\n</div>\n", styles: [".preview-root{--p-list-radius: var(--md-sys-shape-corner-medium, 16px);--p-list-shadow: var(--md-sys-elevation-level2);--p-list-border: 1px solid var(--md-sys-color-outline-variant);--p-list-blur: 10px;--p-list-foreground: var(--md-sys-color-on-surface);--p-list-foreground-muted: var(--md-sys-color-on-surface-variant);--p-list-grad-foreground: var( --md-sys-color-on-primary-container, var(--md-sys-color-on-surface) );--p-list-grad-foreground-muted: color-mix( in srgb, var(--p-list-grad-foreground) 76%, transparent );--p-list-surface: var(--md-sys-color-surface-container);--p-list-accent: var(--md-sys-color-primary);--p-list-item-border: 1px solid var(--md-sys-color-outline-variant);--p-list-grad-from: var(--md-sys-color-primary-container);--p-list-grad-to: var(--md-sys-color-tertiary-container);--p-list-grad-angle: 135deg;--p-list-tile-minW: 240px;--p-list-tile-gap: 16px;--p-list-tile-padding: 16px;--p-list-tile-radius: 16px;--p-list-tile-media-radius: 12px;--p-list-tile-media-ratio: 1 / 1;padding:12px;border-radius:12px;overflow:hidden}.preview-light{background:var(--md-sys-color-surface-container-low, var(--md-sys-color-surface))}.preview-dark{background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface))}.preview-grid{background-color:var(--md-sys-color-surface-variant, var(--md-sys-color-surface));background-image:linear-gradient(var(--md-sys-color-outline-variant, var(--md-sys-color-outline)) 1px,transparent 1px),linear-gradient(90deg,var(--md-sys-color-outline-variant, var(--md-sys-color-outline)) 1px,transparent 1px);background-size:16px 16px}.skin-elevated,.skin-outline,.skin-flat,.skin-neumorphism{--p-list-item-surface: var(--mdc-elevated-card-container-color, var(--p-list-surface));--p-list-item-border: var(--p-list-border)}.skin-elevated .item-card,.skin-outline .item-card,.skin-flat .item-card,.skin-neumorphism .item-card{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:var(--p-list-radius);box-shadow:var(--p-list-shadow);border:var(--p-list-border)}.skin-elevated .item-tile,.skin-outline .item-tile,.skin-flat .item-tile,.skin-neumorphism .item-tile{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));box-shadow:var(--p-list-shadow);border:var(--p-list-border)}.skin-elevated .mat-mdc-list-item .list-item-content,.skin-outline .mat-mdc-list-item .list-item-content,.skin-flat .mat-mdc-list-item .list-item-content,.skin-neumorphism .mat-mdc-list-item .list-item-content{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * .75);box-shadow:var(--p-list-shadow);border:var(--p-list-item-border);color:var(--p-list-foreground)}.skin-outline{--p-list-shadow: none;--p-list-border: 1px solid var(--md-sys-color-outline)}.skin-flat{--p-list-shadow: none;--p-list-border: 1px solid var(--md-sys-color-outline-variant)}.skin-neumorphism{--p-list-shadow: var(--md-sys-elevation-level2);--p-list-border: 1px solid var(--md-sys-color-outline-variant);--p-list-radius: 1.25rem}.skin-pill-soft .item-card,.skin-pill-soft .item-tile{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:var(--md-sys-elevation-level1);border:var(--p-list-border)}.skin-pill-soft .mat-mdc-list-item .list-item-content{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:var(--md-sys-elevation-level1);border:var(--p-list-border);color:var(--p-list-foreground)}.lead-image{position:relative;width:96px;height:72px;border-radius:12px;overflow:hidden}.lead-image img{width:100%;height:100%;object-fit:cover;display:block}.lead-image .lead-badge{position:absolute;left:8px;bottom:8px}.features{display:flex;flex-wrap:wrap;gap:8px;margin-top:2px;color:var(--p-list-foreground-muted);min-width:0}.feature{display:inline-flex;align-items:center;gap:4px;min-width:0;max-width:100%;font-size:.75rem;line-height:1.25}.feature mat-icon{flex:0 0 auto;width:16px;height:16px;font-size:16px}.model-media .lead-image{width:136px;height:96px;border-radius:16px}.model-hotel .lead-image{width:160px;height:110px;border-radius:18px}.skin-gradient-tile{--p-list-foreground: var(--p-list-grad-foreground);--p-list-foreground-muted: var(--p-list-grad-foreground-muted)}.skin-gradient-tile .item-card,.skin-gradient-tile .item-tile{background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));border-radius:var(--p-list-radius);color:var(--p-list-foreground)}.skin-gradient-tile .tile-media{background:color-mix(in srgb,var(--md-sys-color-surface) 74%,transparent);border-color:color-mix(in srgb,var(--p-list-foreground) 22%,transparent)}.skin-gradient-tile .tile-media mat-icon,.skin-gradient-tile .tile-meta,.skin-gradient-tile .tile-trailing,.skin-gradient-tile .feature,.skin-gradient-tile .secondary,.skin-gradient-tile .meta{color:var(--p-list-foreground-muted)}.skin-gradient-tile .tile-status,.skin-gradient-tile [data-rich-node-type=badge] .prx-rich-badge{color:var(--md-sys-color-on-secondary-container, var(--p-list-foreground));background:var(--md-sys-color-secondary-container, rgba(255, 255, 255, .72))}.skin-gradient-tile .mat-mdc-list-item .list-item-content{background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));border-radius:var(--p-list-radius);color:var(--p-list-foreground)}.skin-glass .item-card,.skin-glass .item-tile{background:var(--md-sys-color-surface-container-low);border-radius:var(--p-list-radius);border:1px solid var(--md-sys-color-outline-variant);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur))}.skin-glass .mat-mdc-list-item .list-item-content{background:var(--md-sys-color-surface-container-low);border-radius:var(--p-list-radius);border:1px solid var(--md-sys-color-outline-variant);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur));color:var(--p-list-foreground)}.list-item-content{display:grid;grid-template-columns:auto minmax(0,1fr) auto auto;align-items:center;gap:12px;padding:12px 16px;min-width:0}.primary{font-weight:600;color:var(--p-list-foreground);min-width:0;overflow:hidden;text-overflow:ellipsis}.secondary{color:var(--p-list-foreground-muted);min-width:0;overflow:hidden}.meta{color:var(--p-list-foreground-muted);min-width:0}.trailing{color:var(--p-list-foreground-muted);text-align:end;min-width:0}.cards-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(var(--p-list-tile-minW),1fr));gap:var(--p-list-tile-gap)}.item-card{position:relative;display:grid;min-height:236px;padding:10px;overflow:hidden;color:var(--p-list-foreground)}.item-card .list-item-content{display:block;grid-template-columns:minmax(0,1fr);align-items:stretch;gap:8px;padding:0;min-width:0;overflow:hidden}.item-card .list-item-text{display:grid;gap:8px;min-width:0;width:100%}.item-card .lead-image{width:100%;height:auto;aspect-ratio:16/9;max-height:104px;border-radius:10px}.item-card .lead-image .lead-badge{max-width:calc(100% - 16px)}.item-card .primary{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-height:1.28;min-width:0;overflow-wrap:anywhere}.item-card .secondary{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;font-size:.78rem;line-height:1.3;color:color-mix(in srgb,var(--p-list-foreground) 72%,transparent);min-width:0;overflow-wrap:anywhere}.item-card .meta{display:inline-flex;align-items:center;gap:2px;max-width:100%;overflow:hidden;white-space:nowrap;font-size:.76rem;line-height:1.2;color:color-mix(in srgb,var(--p-list-foreground) 72%,transparent)}.item-card .meta mat-icon{flex:0 0 auto;width:16px;height:16px;font-size:16px}.item-card .trailing{justify-self:start;max-width:100%;text-align:start;color:inherit}.item-card .features{color:color-mix(in srgb,var(--p-list-foreground) 72%,transparent);min-width:0}.item-card .feature{min-width:0;max-width:100%;overflow-wrap:anywhere}.item-card .mat-mdc-chip{max-width:100%}.preview-root ::ng-deep praxis-rich-content .prx-rich-node[data-rich-node-type=badge]{background:transparent;color:inherit}.preview-root ::ng-deep praxis-rich-content .prx-rich-node[data-rich-node-type=badge] .prx-rich-badge{min-height:22px;padding:1px 9px;border:1px solid var(--p-list-chip-border-color, transparent);background:var(--p-list-chip-background, var(--md-sys-color-primary-container, #e8def8));color:var(--p-list-chip-color, var(--md-sys-color-on-primary-container, #21005d));line-height:18px}.item-card .mdc-evolution-chip__text-label,.item-card [data-rich-node-type=badge]{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tiles-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(var(--p-list-tile-minW),1fr));gap:var(--p-list-tile-gap)}.item-tile{padding:var(--p-list-tile-padding);border-radius:var(--p-list-tile-radius);border:var(--p-list-border);background:var(--p-list-surface);display:grid;gap:8px}.tile-media{width:100%;aspect-ratio:var(--p-list-tile-media-ratio);border-radius:var(--p-list-tile-media-radius);background:var(--md-sys-color-surface-container-low, var(--p-list-surface));display:grid;place-items:center;overflow:hidden}.tile-media img{width:100%;height:100%;object-fit:cover}.tile-body{display:grid;gap:4px}.chip-outlined.mat-mdc-chip{background:transparent!important;border:1px solid currentColor}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i2.MatList, selector: "mat-list", exportAs: ["matList"] }, { kind: "component", type: i2.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i5.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["role", "id", "aria-label", "aria-description", "value", "color", "removable", "highlighted", "disableRipple", "disabled"], outputs: ["removed", "destroyed"], exportAs: ["matChip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisRichContent, selector: "praxis-rich-content", inputs: ["document", "nodes", "context", "hostCapabilities", "layout", "rootClassName"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1751
1755
|
}
|
|
1752
1756
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisListSkinPreviewComponent, decorators: [{
|
|
1753
1757
|
type: Component,
|
|
@@ -1775,6 +1779,8 @@ const PRAXIS_LIST_EN_US = {
|
|
|
1775
1779
|
emptyState: 'No items available',
|
|
1776
1780
|
emptyStateConnectResource: 'Connect this list to a resource to load items.',
|
|
1777
1781
|
recordOpenUnavailable: 'Unable to open details for this item.',
|
|
1782
|
+
'selection.ariaLabel': 'Selectable list',
|
|
1783
|
+
'selection.selectItem': 'Select item',
|
|
1778
1784
|
'toolbar.label': 'List controls',
|
|
1779
1785
|
'toolbar.search': 'Search',
|
|
1780
1786
|
'toolbar.sort': 'Sort by',
|
|
@@ -1834,7 +1840,7 @@ const PRAXIS_LIST_EN_US = {
|
|
|
1834
1840
|
'Use custom color': 'Use custom color',
|
|
1835
1841
|
Default: 'Default',
|
|
1836
1842
|
"Show when (e.g.: ${item.status} == 'done')": "Show when (e.g.: ${item.status} == 'done')",
|
|
1837
|
-
|
|
1843
|
+
'Supported syntax: "${item.field} == \'value\'". Advanced expressions are not evaluated.': 'Supported syntax: "${item.field} == \'value\'". Advanced expressions are not evaluated.',
|
|
1838
1844
|
Action: 'Action',
|
|
1839
1845
|
Preview: 'Preview',
|
|
1840
1846
|
Remove: 'Remove',
|
|
@@ -2039,7 +2045,7 @@ const PRAXIS_LIST_EN_US = {
|
|
|
2039
2045
|
Width: 'Width',
|
|
2040
2046
|
'Min width': 'Min width',
|
|
2041
2047
|
'Max width': 'Max width',
|
|
2042
|
-
|
|
2048
|
+
Justify: 'Justify',
|
|
2043
2049
|
'Move column left': 'Move column left',
|
|
2044
2050
|
'Move column right': 'Move column right',
|
|
2045
2051
|
'Remove column': 'Remove column',
|
|
@@ -2149,6 +2155,8 @@ const PRAXIS_LIST_PT_BR = {
|
|
|
2149
2155
|
emptyState: 'Nenhum item disponível',
|
|
2150
2156
|
emptyStateConnectResource: 'Conecte a lista a um recurso para carregar os itens.',
|
|
2151
2157
|
recordOpenUnavailable: 'Não foi possível abrir os detalhes deste item.',
|
|
2158
|
+
'selection.ariaLabel': 'Lista selecionável',
|
|
2159
|
+
'selection.selectItem': 'Selecionar item',
|
|
2152
2160
|
'toolbar.label': 'Controles da lista',
|
|
2153
2161
|
'toolbar.search': 'Buscar',
|
|
2154
2162
|
'toolbar.sort': 'Ordenar por',
|
|
@@ -2208,7 +2216,7 @@ const PRAXIS_LIST_PT_BR = {
|
|
|
2208
2216
|
'Use custom color': 'Usar cor personalizada',
|
|
2209
2217
|
Default: 'Padrão',
|
|
2210
2218
|
"Show when (e.g.: ${item.status} == 'done')": "Exibir quando (ex.: ${item.status} == 'concluído')",
|
|
2211
|
-
|
|
2219
|
+
'Supported syntax: "${item.field} == \'value\'". Advanced expressions are not evaluated.': 'Sintaxe suportada: "${item.field} == \'valor\'". Expressões avançadas não são avaliadas.',
|
|
2212
2220
|
Action: 'Ação',
|
|
2213
2221
|
Preview: 'Pré-visualização',
|
|
2214
2222
|
Remove: 'Remover',
|
|
@@ -2413,7 +2421,7 @@ const PRAXIS_LIST_PT_BR = {
|
|
|
2413
2421
|
Width: 'Largura',
|
|
2414
2422
|
'Min width': 'Largura mínima',
|
|
2415
2423
|
'Max width': 'Largura máxima',
|
|
2416
|
-
|
|
2424
|
+
Justify: 'Justificar',
|
|
2417
2425
|
'Move column left': 'Mover coluna para a esquerda',
|
|
2418
2426
|
'Move column right': 'Mover coluna para a direita',
|
|
2419
2427
|
'Remove column': 'Remover coluna',
|
|
@@ -2542,7 +2550,7 @@ class PraxisMetaEditorTextComponent {
|
|
|
2542
2550
|
this.change.emit();
|
|
2543
2551
|
}
|
|
2544
2552
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisMetaEditorTextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2545
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.14", type: PraxisMetaEditorTextComponent, isStandalone: true, selector: "praxis-meta-editor-text", inputs: { model: "model", setPipe: "setPipe" }, outputs: { change: "change" }, ngImport: i0, template: "<div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Pipes / Format') }}</mat-label>\n <input matInput [(ngModel)]=\"model.extraPipe\" (ngModelChange)=\"change.emit()\" />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'build'\"\n size=\"dense\"\n [matMenuTriggerFor]=\"pipeMenu\"\n [attr.aria-label]=\"tx('Open formatting menu')\"\n ></button>\n </mat-form-field>\n <mat-menu #pipeMenu=\"matMenu\">\n <button mat-menu-item (click)=\"onPipe('map:KEY=VALUE')\"><mat-icon>tune</mat-icon>{{ tx('Map') }}</button>\n </mat-menu>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3
|
|
2553
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.14", type: PraxisMetaEditorTextComponent, isStandalone: true, selector: "praxis-meta-editor-text", inputs: { model: "model", setPipe: "setPipe" }, outputs: { change: "change" }, ngImport: i0, template: "<div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Pipes / Format') }}</mat-label>\n <input matInput [(ngModel)]=\"model.extraPipe\" (ngModelChange)=\"change.emit()\" />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'build'\"\n size=\"dense\"\n [matMenuTriggerFor]=\"pipeMenu\"\n [attr.aria-label]=\"tx('Open formatting menu')\"\n ></button>\n </mat-form-field>\n <mat-menu #pipeMenu=\"matMenu\">\n <button mat-menu-item (click)=\"onPipe('map:KEY=VALUE')\"><mat-icon>tune</mat-icon>{{ tx('Map') }}</button>\n </mat-menu>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.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: MatMenuModule }, { kind: "component", type: i5$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: i5$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i5$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }] });
|
|
2546
2554
|
}
|
|
2547
2555
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisMetaEditorTextComponent, decorators: [{
|
|
2548
2556
|
type: Component,
|
|
@@ -2563,7 +2571,7 @@ class PraxisMetaEditorChipComponent {
|
|
|
2563
2571
|
enableCustomColor;
|
|
2564
2572
|
change = new EventEmitter();
|
|
2565
2573
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisMetaEditorChipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2566
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisMetaEditorChipComponent, isStandalone: true, selector: "praxis-meta-editor-chip", inputs: { model: "model", paletteOptions: "paletteOptions", colorDotBackground: "colorDotBackground", isCustomColor: "isCustomColor", enableCustomColor: "enableCustomColor" }, outputs: { change: "change" }, ngImport: i0, template: "<div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Cor</mat-label>\n <mat-select [(ngModel)]=\"model.chipColor\" (ngModelChange)=\"change.emit()\">\n @for (c of paletteOptions; track c.value || 'default') {\n <mat-option [value]=\"c.value\">\n <span class=\"color-dot\" [style.background]=\"colorDotBackground?.(c.value)\"></span>{{ c.label }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Variante</mat-label>\n <mat-select [(ngModel)]=\"model.chipVariant\" (ngModelChange)=\"change.emit()\">\n <mat-option value=\"filled\">Filled</mat-option>\n <mat-option value=\"outlined\">Outlined</mat-option>\n </mat-select>\n </mat-form-field>\n</div>\n<div class=\"g gap-8\">\n @if (isCustomColor?.(model.chipColor)) {\n <div>\n <pdx-color-picker label=\"Cor personalizada\" [format]=\"'hex'\" [(ngModel)]=\"model.chipColor\"\n (ngModelChange)=\"change.emit()\"></pdx-color-picker>\n </div>\n } @else {\n <button mat-stroked-button type=\"button\" (click)=\"enableCustomColor?.(model, 'chipColor'); change.emit()\">\n Usar cor personalizada\n </button>\n }\n</div>\n<div class=\"g row-flow gap-8 mt-8\">\n <span class=\"text-caption muted\">Presets</span>\n <button mat-stroked-button type=\"button\" (click)=\"model.chipVariant='filled'; model.chipColor='primary'; change.emit()\">Status</button>\n <button mat-stroked-button type=\"button\" (click)=\"model.chipVariant='outlined'; model.chipColor='primary'; change.emit()\">Outlined</button>\n <button mat-stroked-button type=\"button\" (click)=\"model.chipVariant='filled'; model.chipColor='warn'; change.emit()\">Cr\u00EDtico</button>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type:
|
|
2574
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisMetaEditorChipComponent, isStandalone: true, selector: "praxis-meta-editor-chip", inputs: { model: "model", paletteOptions: "paletteOptions", colorDotBackground: "colorDotBackground", isCustomColor: "isCustomColor", enableCustomColor: "enableCustomColor" }, outputs: { change: "change" }, ngImport: i0, template: "<div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Cor</mat-label>\n <mat-select [(ngModel)]=\"model.chipColor\" (ngModelChange)=\"change.emit()\">\n @for (c of paletteOptions; track c.value || 'default') {\n <mat-option [value]=\"c.value\">\n <span class=\"color-dot\" [style.background]=\"colorDotBackground?.(c.value)\"></span>{{ c.label }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Variante</mat-label>\n <mat-select [(ngModel)]=\"model.chipVariant\" (ngModelChange)=\"change.emit()\">\n <mat-option value=\"filled\">Filled</mat-option>\n <mat-option value=\"outlined\">Outlined</mat-option>\n </mat-select>\n </mat-form-field>\n</div>\n<div class=\"g gap-8\">\n @if (isCustomColor?.(model.chipColor)) {\n <div>\n <pdx-color-picker label=\"Cor personalizada\" [format]=\"'hex'\" [(ngModel)]=\"model.chipColor\"\n (ngModelChange)=\"change.emit()\"></pdx-color-picker>\n </div>\n } @else {\n <button mat-stroked-button type=\"button\" (click)=\"enableCustomColor?.(model, 'chipColor'); change.emit()\">\n Usar cor personalizada\n </button>\n }\n</div>\n<div class=\"g row-flow gap-8 mt-8\">\n <span class=\"text-caption muted\">Presets</span>\n <button mat-stroked-button type=\"button\" (click)=\"model.chipVariant='filled'; model.chipColor='primary'; change.emit()\">Status</button>\n <button mat-stroked-button type=\"button\" (click)=\"model.chipVariant='outlined'; model.chipColor='primary'; change.emit()\">Outlined</button>\n <button mat-stroked-button type=\"button\" (click)=\"model.chipVariant='filled'; model.chipColor='warn'; change.emit()\">Cr\u00EDtico</button>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i7.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: PdxColorPickerComponent, selector: "pdx-color-picker", inputs: ["actionsLayout", "activeView", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "clearButton", "disabledMode", "readonlyMode", "visible", "presentationMode", "fillMode", "format", "gradientSettings", "icon", "iconClass", "svgIcon", "paletteSettings", "popupSettings", "preview", "rounded", "size", "tabindex", "views", "maxRecent", "showRecent"], outputs: ["valueChange", "open", "close", "cancel", "activeViewChange", "activeColorClick", "focusEvent", "blurEvent"] }] });
|
|
2567
2575
|
}
|
|
2568
2576
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisMetaEditorChipComponent, decorators: [{
|
|
2569
2577
|
type: Component,
|
|
@@ -2590,7 +2598,7 @@ class PraxisMetaEditorRatingComponent {
|
|
|
2590
2598
|
enableCustomColor;
|
|
2591
2599
|
change = new EventEmitter();
|
|
2592
2600
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisMetaEditorRatingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2593
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisMetaEditorRatingComponent, isStandalone: true, selector: "praxis-meta-editor-rating", inputs: { model: "model", paletteOptions: "paletteOptions", colorDotBackground: "colorDotBackground", isCustomColor: "isCustomColor", enableCustomColor: "enableCustomColor" }, outputs: { change: "change" }, ngImport: i0, template: "<div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Cor</mat-label>\n <mat-select [(ngModel)]=\"model.ratingColor\" (ngModelChange)=\"change.emit()\">\n @for (c of paletteOptions; track c.value || 'default') {\n <mat-option [value]=\"c.value\">\n <span class=\"color-dot\" [style.background]=\"colorDotBackground?.(c.value)\"></span>{{ c.label }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Tamanho (px)</mat-label>\n <input matInput type=\"number\" min=\"10\" max=\"32\" [(ngModel)]=\"model.ratingSize\" (ngModelChange)=\"change.emit()\" />\n @if (model.ratingSize!=null && (model.ratingSize < 10 || model.ratingSize > 32)) {\n <mat-error>Tamanho 10\u201332</mat-error>\n }\n </mat-form-field>\n</div>\n<div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>M\u00E1ximo</mat-label>\n <input matInput type=\"number\" min=\"1\" max=\"10\" [(ngModel)]=\"model.ratingMax\" (ngModelChange)=\"change.emit()\" />\n @if (model.ratingMax!=null && (model.ratingMax < 1 || model.ratingMax > 10)) {\n <mat-error>M\u00E1ximo 1\u201310</mat-error>\n }\n </mat-form-field>\n <div class=\"muted text-caption\">Padr\u00E3o: 5</div>\n</div>\n<div class=\"g gap-8\">\n @if (isCustomColor?.(model.ratingColor)) {\n <div>\n <pdx-color-picker label=\"Cor personalizada\" [format]=\"'hex'\" [(ngModel)]=\"model.ratingColor\"\n (ngModelChange)=\"change.emit()\"></pdx-color-picker>\n </div>\n } @else {\n <button mat-stroked-button type=\"button\" (click)=\"enableCustomColor?.(model, 'ratingColor'); change.emit()\">\n Usar cor personalizada\n </button>\n }\n</div>\n<div class=\"g row-flow gap-8 mt-8\">\n <span class=\"text-caption muted\">Presets</span>\n <button mat-stroked-button type=\"button\" (click)=\"model.ratingSize=12; model.ratingMax=5; model.ratingColor='primary'; change.emit()\">Compacto</button>\n <button mat-stroked-button type=\"button\" (click)=\"model.ratingSize=16; model.ratingMax=5; model.ratingColor='primary'; change.emit()\">Padr\u00E3o</button>\n <button mat-stroked-button type=\"button\" (click)=\"model.ratingSize=20; model.ratingMax=5; model.ratingColor='accent'; change.emit()\">Destaque</button>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { 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: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3
|
|
2601
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisMetaEditorRatingComponent, isStandalone: true, selector: "praxis-meta-editor-rating", inputs: { model: "model", paletteOptions: "paletteOptions", colorDotBackground: "colorDotBackground", isCustomColor: "isCustomColor", enableCustomColor: "enableCustomColor" }, outputs: { change: "change" }, ngImport: i0, template: "<div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Cor</mat-label>\n <mat-select [(ngModel)]=\"model.ratingColor\" (ngModelChange)=\"change.emit()\">\n @for (c of paletteOptions; track c.value || 'default') {\n <mat-option [value]=\"c.value\">\n <span class=\"color-dot\" [style.background]=\"colorDotBackground?.(c.value)\"></span>{{ c.label }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Tamanho (px)</mat-label>\n <input matInput type=\"number\" min=\"10\" max=\"32\" [(ngModel)]=\"model.ratingSize\" (ngModelChange)=\"change.emit()\" />\n @if (model.ratingSize!=null && (model.ratingSize < 10 || model.ratingSize > 32)) {\n <mat-error>Tamanho 10\u201332</mat-error>\n }\n </mat-form-field>\n</div>\n<div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>M\u00E1ximo</mat-label>\n <input matInput type=\"number\" min=\"1\" max=\"10\" [(ngModel)]=\"model.ratingMax\" (ngModelChange)=\"change.emit()\" />\n @if (model.ratingMax!=null && (model.ratingMax < 1 || model.ratingMax > 10)) {\n <mat-error>M\u00E1ximo 1\u201310</mat-error>\n }\n </mat-form-field>\n <div class=\"muted text-caption\">Padr\u00E3o: 5</div>\n</div>\n<div class=\"g gap-8\">\n @if (isCustomColor?.(model.ratingColor)) {\n <div>\n <pdx-color-picker label=\"Cor personalizada\" [format]=\"'hex'\" [(ngModel)]=\"model.ratingColor\"\n (ngModelChange)=\"change.emit()\"></pdx-color-picker>\n </div>\n } @else {\n <button mat-stroked-button type=\"button\" (click)=\"enableCustomColor?.(model, 'ratingColor'); change.emit()\">\n Usar cor personalizada\n </button>\n }\n</div>\n<div class=\"g row-flow gap-8 mt-8\">\n <span class=\"text-caption muted\">Presets</span>\n <button mat-stroked-button type=\"button\" (click)=\"model.ratingSize=12; model.ratingMax=5; model.ratingColor='primary'; change.emit()\">Compacto</button>\n <button mat-stroked-button type=\"button\" (click)=\"model.ratingSize=16; model.ratingMax=5; model.ratingColor='primary'; change.emit()\">Padr\u00E3o</button>\n <button mat-stroked-button type=\"button\" (click)=\"model.ratingSize=20; model.ratingMax=5; model.ratingColor='accent'; change.emit()\">Destaque</button>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { 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: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.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: i4$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i7.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: PdxColorPickerComponent, selector: "pdx-color-picker", inputs: ["actionsLayout", "activeView", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "clearButton", "disabledMode", "readonlyMode", "visible", "presentationMode", "fillMode", "format", "gradientSettings", "icon", "iconClass", "svgIcon", "paletteSettings", "popupSettings", "preview", "rounded", "size", "tabindex", "views", "maxRecent", "showRecent"], outputs: ["valueChange", "open", "close", "cancel", "activeViewChange", "activeColorClick", "focusEvent", "blurEvent"] }] });
|
|
2594
2602
|
}
|
|
2595
2603
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisMetaEditorRatingComponent, decorators: [{
|
|
2596
2604
|
type: Component,
|
|
@@ -2613,7 +2621,7 @@ class PraxisMetaEditorCurrencyComponent {
|
|
|
2613
2621
|
model;
|
|
2614
2622
|
change = new EventEmitter();
|
|
2615
2623
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisMetaEditorCurrencyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2616
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.14", type: PraxisMetaEditorCurrencyComponent, isStandalone: true, selector: "praxis-meta-editor-currency", inputs: { model: "model" }, outputs: { change: "change" }, ngImport: i0, template: "<div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Moeda</mat-label>\n <input matInput [(ngModel)]=\"model.currencyCode\" (ngModelChange)=\"change.emit()\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Locale</mat-label>\n <input matInput [(ngModel)]=\"model.locale\" (ngModelChange)=\"change.emit()\" />\n </mat-form-field>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3
|
|
2624
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.14", type: PraxisMetaEditorCurrencyComponent, isStandalone: true, selector: "praxis-meta-editor-currency", inputs: { model: "model" }, outputs: { change: "change" }, ngImport: i0, template: "<div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Moeda</mat-label>\n <input matInput [(ngModel)]=\"model.currencyCode\" (ngModelChange)=\"change.emit()\" />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Locale</mat-label>\n <input matInput [(ngModel)]=\"model.locale\" (ngModelChange)=\"change.emit()\" />\n </mat-form-field>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.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"] }] });
|
|
2617
2625
|
}
|
|
2618
2626
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisMetaEditorCurrencyComponent, decorators: [{
|
|
2619
2627
|
type: Component,
|
|
@@ -2628,7 +2636,7 @@ class PraxisMetaEditorDateComponent {
|
|
|
2628
2636
|
model;
|
|
2629
2637
|
change = new EventEmitter();
|
|
2630
2638
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisMetaEditorDateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2631
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.14", type: PraxisMetaEditorDateComponent, isStandalone: true, selector: "praxis-meta-editor-date", inputs: { model: "model" }, outputs: { change: "change" }, ngImport: i0, template: "<div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Formato</mat-label>\n <mat-select [(ngModel)]=\"model.dateStyle\" (ngModelChange)=\"change.emit()\">\n <mat-option value=\"short\">Curto</mat-option>\n <mat-option value=\"medium\">M\u00E9dio</mat-option>\n <mat-option value=\"long\">Longo</mat-option>\n <mat-option value=\"full\">Completo</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Locale</mat-label>\n <input matInput [(ngModel)]=\"model.locale\" (ngModelChange)=\"change.emit()\" />\n </mat-form-field>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3
|
|
2639
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.14", type: PraxisMetaEditorDateComponent, isStandalone: true, selector: "praxis-meta-editor-date", inputs: { model: "model" }, outputs: { change: "change" }, ngImport: i0, template: "<div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Formato</mat-label>\n <mat-select [(ngModel)]=\"model.dateStyle\" (ngModelChange)=\"change.emit()\">\n <mat-option value=\"short\">Curto</mat-option>\n <mat-option value=\"medium\">M\u00E9dio</mat-option>\n <mat-option value=\"long\">Longo</mat-option>\n <mat-option value=\"full\">Completo</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Locale</mat-label>\n <input matInput [(ngModel)]=\"model.locale\" (ngModelChange)=\"change.emit()\" />\n </mat-form-field>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.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: i4$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }] });
|
|
2632
2640
|
}
|
|
2633
2641
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisMetaEditorDateComponent, decorators: [{
|
|
2634
2642
|
type: Component,
|
|
@@ -2647,7 +2655,7 @@ class PraxisMetaEditorIconComponent {
|
|
|
2647
2655
|
enableCustomColor;
|
|
2648
2656
|
change = new EventEmitter();
|
|
2649
2657
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisMetaEditorIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2650
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisMetaEditorIconComponent, isStandalone: true, selector: "praxis-meta-editor-icon", inputs: { model: "model", paletteOptions: "paletteOptions", colorDotBackground: "colorDotBackground", isCustomColor: "isCustomColor", enableCustomColor: "enableCustomColor" }, outputs: { change: "change" }, ngImport: i0, template: "<div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Cor</mat-label>\n <mat-select [(ngModel)]=\"model.iconColor\" (ngModelChange)=\"change.emit()\">\n @for (c of paletteOptions; track c.value || 'default') {\n <mat-option [value]=\"c.value\">\n <span class=\"color-dot\" [style.background]=\"colorDotBackground?.(c.value)\"></span>{{ c.label }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n<div class=\"g gap-8\">\n @if (isCustomColor?.(model.iconColor)) {\n <div>\n <pdx-color-picker label=\"Cor personalizada\" [format]=\"'hex'\" [(ngModel)]=\"model.iconColor\"\n (ngModelChange)=\"change.emit()\"></pdx-color-picker>\n </div>\n } @else {\n <button mat-stroked-button type=\"button\" (click)=\"enableCustomColor?.(model, 'iconColor'); change.emit()\">\n Usar cor personalizada\n </button>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type:
|
|
2658
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisMetaEditorIconComponent, isStandalone: true, selector: "praxis-meta-editor-icon", inputs: { model: "model", paletteOptions: "paletteOptions", colorDotBackground: "colorDotBackground", isCustomColor: "isCustomColor", enableCustomColor: "enableCustomColor" }, outputs: { change: "change" }, ngImport: i0, template: "<div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Cor</mat-label>\n <mat-select [(ngModel)]=\"model.iconColor\" (ngModelChange)=\"change.emit()\">\n @for (c of paletteOptions; track c.value || 'default') {\n <mat-option [value]=\"c.value\">\n <span class=\"color-dot\" [style.background]=\"colorDotBackground?.(c.value)\"></span>{{ c.label }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n<div class=\"g gap-8\">\n @if (isCustomColor?.(model.iconColor)) {\n <div>\n <pdx-color-picker label=\"Cor personalizada\" [format]=\"'hex'\" [(ngModel)]=\"model.iconColor\"\n (ngModelChange)=\"change.emit()\"></pdx-color-picker>\n </div>\n } @else {\n <button mat-stroked-button type=\"button\" (click)=\"enableCustomColor?.(model, 'iconColor'); change.emit()\">\n Usar cor personalizada\n </button>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i7.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: PdxColorPickerComponent, selector: "pdx-color-picker", inputs: ["actionsLayout", "activeView", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "clearButton", "disabledMode", "readonlyMode", "visible", "presentationMode", "fillMode", "format", "gradientSettings", "icon", "iconClass", "svgIcon", "paletteSettings", "popupSettings", "preview", "rounded", "size", "tabindex", "views", "maxRecent", "showRecent"], outputs: ["valueChange", "open", "close", "cancel", "activeViewChange", "activeColorClick", "focusEvent", "blurEvent"] }] });
|
|
2651
2659
|
}
|
|
2652
2660
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisMetaEditorIconComponent, decorators: [{
|
|
2653
2661
|
type: Component,
|
|
@@ -2670,7 +2678,7 @@ class PraxisMetaEditorImageComponent {
|
|
|
2670
2678
|
model;
|
|
2671
2679
|
change = new EventEmitter();
|
|
2672
2680
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisMetaEditorImageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2673
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.14", type: PraxisMetaEditorImageComponent, isStandalone: true, selector: "praxis-meta-editor-image", inputs: { model: "model" }, outputs: { change: "change" }, ngImport: i0, template: "<div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\" class=\"col-span-2\">\n <mat-label>Alt da imagem</mat-label>\n <input matInput [(ngModel)]=\"model.imageAlt\" (ngModelChange)=\"change.emit()\" />\n </mat-form-field>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3
|
|
2681
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.14", type: PraxisMetaEditorImageComponent, isStandalone: true, selector: "praxis-meta-editor-image", inputs: { model: "model" }, outputs: { change: "change" }, ngImport: i0, template: "<div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\" class=\"col-span-2\">\n <mat-label>Alt da imagem</mat-label>\n <input matInput [(ngModel)]=\"model.imageAlt\" (ngModelChange)=\"change.emit()\" />\n </mat-form-field>\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.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"] }] });
|
|
2674
2682
|
}
|
|
2675
2683
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisMetaEditorImageComponent, decorators: [{
|
|
2676
2684
|
type: Component,
|
|
@@ -4357,7 +4365,7 @@ class PraxisListJsonConfigEditorComponent {
|
|
|
4357
4365
|
</mat-form-field>
|
|
4358
4366
|
</div>
|
|
4359
4367
|
</div>
|
|
4360
|
-
`, isInline: true, styles: [".json-config-editor{display:flex;flex-direction:column;gap:16px}.educational-card{background-color:var(--md-sys-color-surface-container-low);border-left:4px solid var(--md-sys-color-primary)}.card-icon{background-color:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container);font-size:20px;width:40px;height:40px;display:flex;align-items:center;justify-content:center}.json-editor-section{display:flex;flex-direction:column;gap:16px}.json-editor-toolbar{display:flex;gap:12px;padding:12px;background-color:var(--md-sys-color-surface-container-low);border-radius:8px;border:1px solid var(--md-sys-color-outline-variant)}.json-textarea-field{width:100%}.pending-update-note{margin:0;color:var(--md-sys-color-secondary);font-size:.875rem}.json-textarea{font-family:Monaco,Menlo,Ubuntu Mono,Consolas,monospace!important;font-size:13px!important;line-height:1.4!important;min-height:320px!important;white-space:pre!important;overflow-wrap:normal!important;overflow-x:auto!important;resize:none!important}.valid-hint{color:var(--md-sys-color-primary)!important}.warning-hint{color:var(--md-sys-color-secondary)!important}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type:
|
|
4368
|
+
`, isInline: true, styles: [".json-config-editor{display:flex;flex-direction:column;gap:16px}.educational-card{background-color:var(--md-sys-color-surface-container-low);border-left:4px solid var(--md-sys-color-primary)}.card-icon{background-color:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container);font-size:20px;width:40px;height:40px;display:flex;align-items:center;justify-content:center}.json-editor-section{display:flex;flex-direction:column;gap:16px}.json-editor-toolbar{display:flex;gap:12px;padding:12px;background-color:var(--md-sys-color-surface-container-low);border-radius:8px;border:1px solid var(--md-sys-color-outline-variant)}.json-textarea-field{width:100%}.pending-update-note{margin:0;color:var(--md-sys-color-secondary);font-size:.875rem}.json-textarea{font-family:Monaco,Menlo,Ubuntu Mono,Consolas,monospace!important;font-size:13px!important;line-height:1.4!important;min-height:320px!important;white-space:pre!important;overflow-wrap:normal!important;overflow-x:auto!important;resize:none!important}.valid-hint{color:var(--md-sys-color-primary)!important}.warning-hint{color:var(--md-sys-color-secondary)!important}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i7.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: MatCardModule }, { kind: "component", type: i3$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i3$1.MatCardAvatar, selector: "[mat-card-avatar], [matCardAvatar]" }, { kind: "directive", type: i3$1.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i3$1.MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: i3$1.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i2$1.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: i3.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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4361
4369
|
}
|
|
4362
4370
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisListJsonConfigEditorComponent, decorators: [{
|
|
4363
4371
|
type: Component,
|
|
@@ -7400,7 +7408,7 @@ class PraxisListConfigEditor {
|
|
|
7400
7408
|
},
|
|
7401
7409
|
providePraxisI18nConfig(SURFACE_OPEN_I18N_CONFIG),
|
|
7402
7410
|
providePraxisListI18n(),
|
|
7403
|
-
], ngImport: i0, template: "<mat-tab-group class=\"list-editor-tabs\">\n <mat-tab [label]=\"tx('Data')\">\n <ng-template matTabContent>\n <div class=\"editor-content\">\n <div class=\"g g-1-auto gap-8 ai-center\">\n <div class=\"muted\">\n {{ tx('Assistant-applied adjustments replace the entire configuration object.') }}\n </div>\n <button\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"tx('applyConfigFromAdapter does not perform a deep merge. Make sure the adapter sends the full config.')\"\n [attr.aria-label]=\"tx('applyConfigFromAdapter does not perform a deep merge. Make sure the adapter sends the full config.')\"\n ></button>\n </div>\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>{{ tx('Resource (API)') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.dataSource.resourcePath\"\n (ngModelChange)=\"onResourcePathChange($event)\"\n [placeholder]=\"tx('e.g.: users')\"\n />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"tx('Resource endpoint (resourcePath).')\"\n [attr.aria-label]=\"tx('Resource endpoint (resourcePath).')\"\n ></button>\n </mat-form-field>\n <div class=\"grid gap-2 mb-12\">\n <div class=\"muted\">{{ getLocalDataSummary() }}</div>\n @if (getLocalDataPrecedenceWarning(); as warning) {\n <div class=\"muted\">{{ warning }}</div>\n }\n @if (getLocalDataColumns().length) {\n <div class=\"muted\">\n {{ tx('Local columns') }}:\n {{ getLocalDataColumns().join(', ') }}\n </div>\n @for (row of getLocalDataPreviewRows(); track $index) {\n <div class=\"muted\">\n @for (column of getLocalDataColumns().slice(0, 6); track column) {\n <span>{{ column }}={{ row[column] }}</span>\n }\n </div>\n }\n }\n </div>\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>{{ tx('Query (JSON)') }}</mat-label>\n <textarea\n matInput\n rows=\"3\"\n [(ngModel)]=\"queryJson\"\n (ngModelChange)=\"onQueryChanged($event)\"\n [placeholder]=\"queryPlaceholder()\"\n ></textarea>\n @if (!queryError) {\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"tx('Optional. Use valid JSON for initial filters.')\"\n [attr.aria-label]=\"tx('Optional. Use valid JSON for initial filters.')\"\n ></button>\n }\n @if (queryError) {\n <mat-error>{{ queryError }}</mat-error>\n }\n </mat-form-field>\n <div class=\"g g-auto-220 gap-12 ai-end mt-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Sort by') }}</mat-label>\n <mat-select\n [(ngModel)]=\"sortField\"\n (ngModelChange)=\"updateSortConfig()\"\n >\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"tx('Resource base field.')\"\n [attr.aria-label]=\"tx('Resource base field.')\"\n ></button>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Direction') }}</mat-label>\n <mat-select\n [(ngModel)]=\"sortDir\"\n (ngModelChange)=\"updateSortConfig()\"\n >\n <mat-option value=\"asc\">{{ tx('Ascending') }}</mat-option>\n <mat-option value=\"desc\">{{ tx('Descending') }}</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n </ng-template>\n </mat-tab>\n <mat-tab [label]=\"tx('JSON')\">\n <ng-template matTabContent>\n <div class=\"editor-content\">\n <praxis-list-json-config-editor\n [document]=\"document\"\n (documentChange)=\"onJsonConfigChange($event)\"\n (validationChange)=\"onJsonValidationChange($event)\"\n (editorEvent)=\"onJsonEditorEvent($event)\"\n >\n </praxis-list-json-config-editor>\n </div>\n </ng-template>\n </mat-tab>\n <mat-tab [label]=\"tx('Actions')\">\n <ng-template matTabContent>\n <div class=\"editor-content g gap-12\">\n <div class=\"g g-1-auto gap-8 ai-center\">\n <div class=\"muted\">\n {{ tx('Configure per-item action buttons (icon, label, color, visibility)') }}\n </div>\n <button mat-flat-button color=\"primary\" (click)=\"addAction()\">\n {{ tx('Add action') }}\n </button>\n </div>\n <div class=\"g g-1-auto gap-8 ai-center\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Global action (Praxis)') }}</mat-label>\n <mat-select\n [(ngModel)]=\"selectedGlobalActionId\"\n (ngModelChange)=\"onGlobalActionSelected($event)\"\n >\n <mat-option [value]=\"undefined\">{{ tx('-- Select --') }}</mat-option>\n @for (ga of globalActionCatalog; track ga.id) {\n <mat-option [value]=\"ga.id\">\n <mat-icon class=\"option-icon\">{{ ga.icon || \"bolt\" }}</mat-icon>\n {{ ga.label }}\n </mat-option>\n }\n </mat-select>\n @if (!globalActionCatalog.length) {\n <mat-hint\n class=\"text-caption muted\"\n >{{ tx('No global action registered.') }}</mat-hint\n >\n }\n </mat-form-field>\n <div class=\"muted text-caption\">\n {{ tx('Select to add with a structured global action.') }}\n </div>\n </div>\n @for (a of working.actions || []; track $index; let i = $index) {\n <div\n class=\"g g-auto-200 gap-12 ai-end\"\n >\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('ID') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"a.id\"\n (ngModelChange)=\"onActionsChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Action type') }}</mat-label>\n <mat-select [(ngModel)]=\"a.kind\" (ngModelChange)=\"onActionsChanged()\">\n <mat-option value=\"icon\">{{ tx('Icon') }}</mat-option>\n <mat-option value=\"button\">{{ tx('Button') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Icon') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"a.icon\"\n (ngModelChange)=\"onActionsChanged()\"\n [placeholder]=\"tx('e.g.: edit, delete')\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Global action') }}</mat-label>\n <mat-select\n [ngModel]=\"getGlobalActionId(a)\"\n (ngModelChange)=\"onActionGlobalActionIdChange(a, $event)\"\n >\n <mat-option value=\"\">{{ tx('None') }}</mat-option>\n @for (action of globalActionCatalog; track action.id) {\n <mat-option [value]=\"action.id\">\n {{ action.label || action.id }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Label') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"a.label\"\n (ngModelChange)=\"onActionsChanged()\"\n />\n </mat-form-field>\n @if (a.kind === 'button') {\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Variant') }}</mat-label>\n <mat-select\n [(ngModel)]=\"a.buttonVariant\"\n (ngModelChange)=\"onActionsChanged()\"\n >\n <mat-option value=\"stroked\">{{ tx('Outlined') }}</mat-option>\n <mat-option value=\"raised\">{{ tx('Raised') }}</mat-option>\n <mat-option value=\"flat\">{{ tx('Filled') }}</mat-option>\n </mat-select>\n </mat-form-field>\n }\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Action color') }}</mat-label>\n <mat-select\n [(ngModel)]=\"a.color\"\n (ngModelChange)=\"onActionsChanged()\"\n >\n @for (c of paletteOptions; track c.value || 'default') {\n <mat-option [value]=\"c.value\">\n <span\n class=\"color-dot\"\n [style.background]=\"colorDotBackground(c.value)\"\n ></span\n >{{ tx(c.label) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n @if (isCustomColor(a.color)) {\n <div\n class=\"g gap-8\"\n >\n <pdx-color-picker\n [label]=\"tx('Custom color')\"\n [format]=\"'hex'\"\n [(ngModel)]=\"a.color\"\n (ngModelChange)=\"onActionsChanged()\"\n ></pdx-color-picker>\n </div>\n } @else {\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"enableCustomActionColor(a)\"\n >\n {{ tx('Use custom color') }}\n </button>\n }\n <mat-form-field appearance=\"outline\" class=\"col-span-2\">\n <mat-label>{{ actionVisibilityLabel() }}</mat-label>\n <textarea\n matInput\n rows=\"3\"\n [ngModel]=\"actionShowIfModel(i)\"\n (ngModelChange)=\"onActionShowIfChanged(i, a, $event)\"\n ></textarea>\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"actionConditionTooltip()\"\n [attr.aria-label]=\"actionConditionTooltip()\"\n ></button>\n </mat-form-field>\n <div class=\"g row-flow gap-8 ai-center\">\n @if ((a.kind || 'icon') === 'icon') {\n <span class=\"action-icon-preview\" aria-hidden=\"true\">\n <mat-icon\n [praxisIcon]=\"a.icon || 'bolt'\"\n [style.cssText]=\"iconStyle(a.color)\"\n ></mat-icon>\n </span>\n }\n @if (a.kind === 'button') {\n @if (a.buttonVariant === 'stroked') {\n <button\n mat-stroked-button\n [color]=\"isThemeColor(a.color) ? a.color : undefined\"\n [style.cssText]=\"buttonStyle(a.color, 'stroked')\"\n >\n {{ a.label || a.id || tx('Action') }}\n </button>\n }\n @if (a.buttonVariant === 'raised') {\n <button\n mat-raised-button\n [color]=\"isThemeColor(a.color) ? a.color : undefined\"\n [style.cssText]=\"buttonStyle(a.color, 'raised')\"\n >\n {{ a.label || a.id || tx('Action') }}\n </button>\n }\n @if (!a.buttonVariant || a.buttonVariant === 'flat') {\n <button\n mat-flat-button\n [color]=\"isThemeColor(a.color) ? a.color : undefined\"\n [style.cssText]=\"buttonStyle(a.color, 'flat')\"\n >\n {{ a.label || a.id || tx('Action') }}\n </button>\n }\n }\n <span class=\"muted\">{{ tx('Preview') }}</span>\n </div>\n <div class=\"flex-end\">\n <button mat-button color=\"warn\" (click)=\"removeAction(i)\">\n {{ tx('Remove') }}\n </button>\n </div>\n <div class=\"g gap-8 col-span-2\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Placement') }}</mat-label>\n <mat-select\n [(ngModel)]=\"a.placement\"\n (ngModelChange)=\"onActionsChanged()\"\n >\n <mat-option [value]=\"undefined\">{{ tx('Default') }}</mat-option>\n <mat-option value=\"trailing\">{{ tx('Trailing') }}</mat-option>\n <mat-option value=\"actions\">{{ tx('Actions column') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-slide-toggle\n [(ngModel)]=\"a.showLoading\"\n (ngModelChange)=\"onActionsChanged()\"\n >{{ tx('Show loading') }}</mat-slide-toggle\n >\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header\n ><mat-panel-title\n >{{ tx('Confirmation') }}</mat-panel-title\n ></mat-expansion-panel-header\n >\n <div class=\"g gap-12 pt-12\">\n <div class=\"g row-flow gap-8 ai-center\">\n <span class=\"text-caption muted\">{{ tx('Type') }}</span>\n <mat-button-toggle-group\n [value]=\"a.confirmation?.type || ''\"\n (change)=\"applyConfirmationPreset(a, $event.value)\"\n >\n <mat-button-toggle value=\"\">{{ tx('Default') }}</mat-button-toggle>\n <mat-button-toggle value=\"danger\">{{ tx('Danger') }}</mat-button-toggle>\n <mat-button-toggle value=\"warning\">{{ tx('Warning') }}</mat-button-toggle>\n <mat-button-toggle value=\"info\">{{ tx('Info') }}</mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Title') }}</mat-label>\n <input\n matInput\n [ngModel]=\"a.confirmation?.title\"\n (ngModelChange)=\"setConfirmationField(a, 'title', $event)\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Message') }}</mat-label>\n <input\n matInput\n [ngModel]=\"a.confirmation?.message\"\n (ngModelChange)=\"setConfirmationField(a, 'message', $event)\"\n />\n </mat-form-field>\n <div class=\"g gap-6\">\n <div class=\"text-caption muted\">{{ tx('Preview') }}</div>\n <div class=\"text-caption\">\n <strong>{{\n a.confirmation?.title || tx('Confirm action')\n }}</strong>\n </div>\n <div class=\"text-caption muted\">\n {{\n a.confirmation?.message ||\n tx('Are you sure you want to continue?')\n }}\n </div>\n <div class=\"text-caption\">\n <span\n class=\"confirm-type\"\n [ngClass]=\"a.confirmation?.type || 'default'\"\n >{{ tx('Type') }}:\n {{ a.confirmation?.type || tx('Default').toLowerCase() }}</span\n >\n </div>\n @if (!a.confirmation?.title && !a.confirmation?.message) {\n <div\n class=\"text-caption muted\"\n >\n {{ tx('Set a title or message for the confirmation.') }}\n </div>\n }\n </div>\n </div>\n </mat-expansion-panel>\n @if (a.globalAction?.actionId) {\n @if (isSurfaceOpenCommand(a)) {\n <div class=\"col-span-2\">\n <praxis-surface-open-action-editor\n [value]=\"getSurfaceOpenGlobalActionPayload(a)\"\n hostKind=\"list\"\n (valueChange)=\"onSurfaceOpenGlobalActionPayloadChange(a, $event)\"\n ></praxis-surface-open-action-editor>\n </div>\n } @else {\n <mat-form-field appearance=\"outline\" class=\"col-span-2\">\n <mat-label>{{ tx('Payload (JSON/Template)') }}</mat-label>\n <textarea\n matInput\n rows=\"4\"\n [ngModel]=\"getGlobalActionPayloadText(a)\"\n (ngModelChange)=\"onGlobalActionPayloadTextChange(a, $event)\"\n placeholder='{\"message\":\"${item.name} favoritado\"}'\n ></textarea>\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"globalActionPayloadSchemaTooltip(a)\"\n [attr.aria-label]=\"globalActionPayloadSchemaTooltip(a)\"\n ></button>\n @if (isGlobalActionPayloadInvalid(a)) {\n <mat-error\n >{{ tx('Invalid JSON') }}</mat-error\n >\n }\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"col-span-2\">\n <mat-label>{{ tx('Payload expression') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getGlobalActionPayloadExprText(a)\"\n (ngModelChange)=\"onGlobalActionPayloadExprTextChange(a, $event)\"\n placeholder=\"item.warningMessage\"\n />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"tx('Advanced escape hatch. When set, payload is resolved from this expression and no default item payload is injected.')\"\n [attr.aria-label]=\"tx('Advanced escape hatch. When set, payload is resolved from this expression and no default item payload is injected.')\"\n ></button>\n </mat-form-field>\n <div class=\"g row-flow gap-8 ai-center\">\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"applyGlobalActionPayloadExample(a)\"\n >\n {{ tx('Insert example') }}\n </button>\n <span class=\"muted text-caption\">{{\n globalActionPayloadExampleHint(a)\n }}</span>\n </div>\n }\n <mat-slide-toggle\n [(ngModel)]=\"a.emitLocal\"\n (ngModelChange)=\"onActionsChanged()\"\n >{{ tx('Emit local event too') }}</mat-slide-toggle\n >\n }\n </div>\n </div>\n }\n </div>\n </ng-template>\n </mat-tab>\n <mat-tab [label]=\"tx('Layout')\">\n <ng-template matTabContent>\n <div class=\"editor-content grid gap-3\">\n <div class=\"preset-row g row-flow gap-8\">\n <button mat-stroked-button (click)=\"applyLayoutPreset('tiles-modern')\">\n {{ tx('Modern tiles preset') }}\n </button>\n </div>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Variant') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.variant\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n <mat-option value=\"list\">{{ tx('List') }}</mat-option>\n <mat-option value=\"cards\">{{ tx('Cards') }}</mat-option>\n <mat-option value=\"tiles\">{{ tx('Tiles') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Model') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.model\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n @if (working.layout.variant === 'list') {\n <mat-option value=\"standard\">{{ tx('Standard') }}</mat-option>\n <mat-option value=\"media\">{{ tx('Media on the left') }}</mat-option>\n <mat-option value=\"hotel\">{{ tx('Hotel (large media)') }}</mat-option>\n } @else {\n @if (working.layout.variant === 'tiles') {\n <mat-option value=\"standard\">{{ tx('Standard tile') }}</mat-option>\n <mat-option value=\"media\">{{ tx('Tile with media') }}</mat-option>\n <mat-option value=\"hotel\">{{ tx('Hotel tile') }}</mat-option>\n } @else {\n <mat-option value=\"standard\">{{ tx('Standard') }}</mat-option>\n <mat-option value=\"media\">{{ tx('Card with media') }}</mat-option>\n <mat-option value=\"hotel\">{{ tx('Hotel') }}</mat-option>\n }\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Lines') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.lines\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n <mat-option [value]=\"1\">1</mat-option>\n <mat-option [value]=\"2\">2</mat-option>\n <mat-option [value]=\"3\">3</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Items per page') }}</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"1\"\n [(ngModel)]=\"working.layout.pageSize\"\n (ngModelChange)=\"onPageSizeChange($event)\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Density') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.density\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n <mat-option value=\"default\">{{ tx('Default') }}</mat-option>\n <mat-option value=\"comfortable\">{{ tx('Comfortable') }}</mat-option>\n <mat-option value=\"compact\">{{ tx('Compact') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Spacing between items') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.itemSpacing\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n <mat-option value=\"none\">{{ tx('No extra space') }}</mat-option>\n <mat-option value=\"tight\">{{ tx('Tight') }}</mat-option>\n <mat-option value=\"default\">{{ tx('Standard') }}</mat-option>\n <mat-option value=\"relaxed\">{{ tx('Relaxed') }}</mat-option>\n </mat-select>\n </mat-form-field>\n @if (working.layout.variant !== 'tiles') {\n <mat-form-field\n appearance=\"outline\"\n >\n <mat-label>{{ tx('Dividers') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.dividers\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n <mat-option value=\"none\">{{ tx('None') }}</mat-option>\n <mat-option value=\"between\">{{ tx('Between groups') }}</mat-option>\n <mat-option value=\"all\">{{ tx('All') }}</mat-option>\n </mat-select>\n </mat-form-field>\n }\n @if (fields.length > 0) {\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Group by') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.groupBy\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n <mat-option [value]=\"\">{{ tx('None') }}</mat-option>\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n } @else {\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Group by') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.layout.groupBy\"\n (ngModelChange)=\"onLayoutChanged()\"\n [placeholder]=\"tx('e.g.: department')\"\n />\n </mat-form-field>\n }\n <mat-slide-toggle\n [(ngModel)]=\"working.layout.stickySectionHeader\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n {{ tx('Sticky section header') }}\n </mat-slide-toggle>\n <mat-slide-toggle\n [(ngModel)]=\"working.layout.virtualScroll\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n {{ tx('Virtual scroll') }}\n </mat-slide-toggle>\n <mat-divider class=\"my-8\"></mat-divider>\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ tx('Row layout') }}</mat-panel-title>\n <mat-panel-description>{{\n tx('Stable columns for list rows')\n }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"grid gap-3\">\n <div class=\"g g-1-auto ai-center gap-12\">\n <mat-slide-toggle\n [ngModel]=\"rowLayoutEnabled()\"\n (ngModelChange)=\"onRowLayoutEnabledChange($event)\"\n >{{ tx('Enable row layout') }}</mat-slide-toggle\n >\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"applyCorporateRowLayoutPreset()\"\n >\n {{ tx('Corporate row preset') }}\n </button>\n </div>\n @if (working.layout.rowLayout) {\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Layout type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.rowLayout.type\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n >\n <mat-option value=\"grid\">{{ tx('Grid') }}</mat-option>\n <mat-option value=\"flex\">{{ tx('Flex') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Vertical alignment') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.rowLayout.itemAlignY\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n >\n @for (align of rowLayoutAlignOptions; track align) {\n <mat-option [value]=\"align\">{{ align }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Gap') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.layout.rowLayout.gap\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n placeholder=\"16px\"\n />\n </mat-form-field>\n </div>\n <div class=\"g g-1-auto ai-center gap-8\">\n <div class=\"muted\">\n {{ tx('Columns') }}\n </div>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n (click)=\"addRowLayoutColumn()\"\n >\n {{ tx('Add column') }}\n </button>\n </div>\n @for (\n column of working.layout.rowLayout.columns || [];\n track $index;\n let i = $index\n ) {\n <div class=\"g g-auto-220 gap-12 ai-end mt-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Slot') }}</mat-label>\n <mat-select\n [(ngModel)]=\"column.slot\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n >\n @for (slot of rowLayoutSlotOptions; track slot) {\n <mat-option [value]=\"slot\">{{ slot }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Width') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"column.width\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n placeholder=\"1fr\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Min width') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"column.minWidth\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n placeholder=\"120px\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Max width') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"column.maxWidth\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n placeholder=\"320px\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Align') }}</mat-label>\n <mat-select\n [(ngModel)]=\"column.align\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n >\n @for (align of rowLayoutColumnAlignOptions; track align) {\n <mat-option [value]=\"align\">{{ align }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Justify') }}</mat-label>\n <mat-select\n [(ngModel)]=\"column.justify\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n >\n @for (justify of rowLayoutJustifyOptions; track justify) {\n <mat-option [value]=\"justify\">{{ justify }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"g row-flow gap-4 ai-center\">\n <button\n type=\"button\"\n [praxisIconButton]=\"'arrow_back'\"\n size=\"dense\"\n [disabled]=\"i === 0\"\n [attr.aria-label]=\"tx('Move column left')\"\n (click)=\"moveRowLayoutColumn(i, -1)\"\n ></button>\n <button\n type=\"button\"\n [praxisIconButton]=\"'arrow_forward'\"\n size=\"dense\"\n [disabled]=\"i === (working.layout.rowLayout.columns?.length || 0) - 1\"\n [attr.aria-label]=\"tx('Move column right')\"\n (click)=\"moveRowLayoutColumn(i, 1)\"\n ></button>\n <button\n type=\"button\"\n [praxisIconButton]=\"'delete'\"\n size=\"dense\"\n class=\"editor-icon-action--danger\"\n [attr.aria-label]=\"tx('Remove column')\"\n (click)=\"removeRowLayoutColumn(i)\"\n ></button>\n </div>\n @if (rowLayoutColumnWarning(column); as warning) {\n <div class=\"muted col-span-2\">\n {{ warning }}\n </div>\n }\n </div>\n }\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ tx('Advanced row CSS') }}</mat-panel-title>\n </mat-expansion-panel-header>\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('CSS class') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.layout.rowLayout.class\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Inline style') }}</mat-label>\n <textarea\n matInput\n rows=\"2\"\n [(ngModel)]=\"working.layout.rowLayout.style\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n ></textarea>\n </mat-form-field>\n </div>\n </mat-expansion-panel>\n }\n </div>\n </mat-expansion-panel>\n <mat-divider class=\"my-8\"></mat-divider>\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ tx('Expansion') }}</mat-panel-title>\n <mat-panel-description>{{\n tx('Inline details for list rows')\n }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"grid gap-3\">\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-slide-toggle\n [ngModel]=\"expansionEnabled()\"\n (ngModelChange)=\"onExpansionEnabledChange($event)\"\n >{{ tx('Enable expansion') }}</mat-slide-toggle\n >\n @if (working.interaction?.expandable) {\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Trigger') }}</mat-label>\n <mat-select\n [ngModel]=\"getExpandTriggerModel()\"\n (ngModelChange)=\"onExpansionInteractionChanged('expandTrigger', $event)\"\n >\n @for (trigger of expandTriggerOptions; track trigger) {\n <mat-option [value]=\"trigger\">{{ trigger }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Mode') }}</mat-label>\n <mat-select\n [ngModel]=\"getExpandModeModel()\"\n (ngModelChange)=\"onExpansionInteractionChanged('expandMode', $event)\"\n >\n @for (mode of expandModeOptions; track mode) {\n <mat-option [value]=\"mode\">{{ mode }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Control placement') }}</mat-label>\n <mat-select\n [ngModel]=\"getExpandPlacementModel()\"\n (ngModelChange)=\"onExpansionInteractionChanged('expandPlacement', $event)\"\n >\n @for (placement of expandPlacementOptions; track placement) {\n <mat-option [value]=\"placement\">{{ placement }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n </div>\n @if (working.interaction?.expandable && working.expansion) {\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Shell') }}</mat-label>\n <mat-select\n [ngModel]=\"getExpansionShellModel()\"\n (ngModelChange)=\"onExpansionRenderingChanged('shell', $event)\"\n >\n @for (shell of expansionShellOptions; track shell) {\n <mat-option [value]=\"shell\">{{ shell }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Columns') }}</mat-label>\n <mat-select\n [ngModel]=\"getExpansionColumnsModel()\"\n (ngModelChange)=\"onExpansionRenderingChanged('columns', $event)\"\n >\n @for (columns of expansionColumnOptions; track columns) {\n <mat-option [value]=\"columns\">{{ columns }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Gap') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getExpansionGapModel()\"\n (ngModelChange)=\"onExpansionRenderingChanged('gap', $event)\"\n placeholder=\"12px\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Padding') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getExpansionPaddingModel()\"\n (ngModelChange)=\"onExpansionRenderingChanged('padding', $event)\"\n placeholder=\"16px\"\n />\n </mat-form-field>\n </div>\n <div class=\"grid gap-2\">\n <div class=\"muted\">{{ getExpansionRemoteSummary() }}</div>\n @for (warning of getExpansionRemoteWarnings(); track warning) {\n <div class=\"muted\">{{ warning }}</div>\n }\n </div>\n <div class=\"g g-1-auto ai-center gap-8\">\n <div class=\"muted\">\n {{ tx('Sections') }}\n </div>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n (click)=\"addExpansionSection()\"\n >\n {{ tx('Add section') }}\n </button>\n </div>\n @for (\n section of working.expansion.sections || [];\n track section.id;\n let i = $index\n ) {\n <div class=\"g g-auto-220 gap-12 ai-end mt-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Id') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"section.id\"\n (ngModelChange)=\"onExpansionChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Title') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"section.title\"\n (ngModelChange)=\"onExpansionChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"section.type\"\n (ngModelChange)=\"onExpansionChanged()\"\n >\n @for (type of expansionSectionTypeOptions; track type) {\n <mat-option [value]=\"type\">{{ type }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Items expression') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"section.itemsExpr\"\n (ngModelChange)=\"onExpansionChanged()\"\n placeholder=\"${item.details}\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Empty label') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"section.emptyLabel\"\n (ngModelChange)=\"onExpansionChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Show when (Json Logic)') }}</mat-label>\n <textarea\n matInput\n rows=\"2\"\n [ngModel]=\"getExpansionSectionShowIfText(section)\"\n (ngModelChange)=\"onExpansionSectionShowIfChanged(section, $event)\"\n ></textarea>\n </mat-form-field>\n <div class=\"g row-flow gap-4 ai-center\">\n <button\n type=\"button\"\n [praxisIconButton]=\"'arrow_upward'\"\n size=\"dense\"\n [disabled]=\"i === 0\"\n [attr.aria-label]=\"tx('Move section up')\"\n (click)=\"moveExpansionSection(i, -1)\"\n ></button>\n <button\n type=\"button\"\n [praxisIconButton]=\"'arrow_downward'\"\n size=\"dense\"\n [disabled]=\"i === (working.expansion.sections?.length || 0) - 1\"\n [attr.aria-label]=\"tx('Move section down')\"\n (click)=\"moveExpansionSection(i, 1)\"\n ></button>\n <button\n type=\"button\"\n [praxisIconButton]=\"'delete'\"\n size=\"dense\"\n class=\"editor-icon-action--danger\"\n [attr.aria-label]=\"tx('Remove section')\"\n (click)=\"removeExpansionSection(i)\"\n ></button>\n </div>\n </div>\n }\n }\n </div>\n </mat-expansion-panel>\n <mat-divider class=\"my-8\"></mat-divider>\n <div class=\"subtitle\">{{ tx('List tools') }}</div>\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-slide-toggle\n [(ngModel)]=\"working.ui.showSearch\"\n (ngModelChange)=\"onUiChanged()\"\n >{{ tx('Show search') }}</mat-slide-toggle\n >\n <mat-slide-toggle\n [(ngModel)]=\"working.ui.showSort\"\n (ngModelChange)=\"onUiChanged()\"\n >{{ tx('Show sorting') }}</mat-slide-toggle\n >\n <mat-slide-toggle\n [(ngModel)]=\"working.ui.showRange\"\n (ngModelChange)=\"onUiChanged()\"\n >{{ tx('Show total X-Y range') }}</mat-slide-toggle\n >\n <mat-slide-toggle\n [ngModel]=\"exportEnabled()\"\n (ngModelChange)=\"onExportEnabledChange($event)\"\n >{{ tx('Enable export') }}</mat-slide-toggle\n >\n </div>\n @if (working.export?.enabled) {\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel mt-12\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ tx('Export') }}</mat-panel-title>\n <mat-panel-description>{{\n tx('Formats, scope and file options')\n }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Formats') }}</mat-label>\n <mat-select\n multiple\n [ngModel]=\"getExportFormatsModel()\"\n (ngModelChange)=\"onExportFormatsChange($event)\"\n >\n @for (format of exportFormatOptions; track format) {\n <mat-option [value]=\"format\">{{\n format.toUpperCase()\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Scope') }}</mat-label>\n <mat-select\n [ngModel]=\"getExportScopeModel()\"\n (ngModelChange)=\"onExportScopeChange($event)\"\n >\n @for (scope of exportScopeOptions; track scope) {\n <mat-option [value]=\"scope\">{{ scope }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Max rows') }}</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"1\"\n [ngModel]=\"getExportMaxRowsModel()\"\n (ngModelChange)=\"onExportMaxRowsChange($event)\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Default file name') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getExportDefaultFileNameModel()\"\n (ngModelChange)=\"onExportDefaultFileNameChange($event)\"\n placeholder=\"praxis-list-export\"\n />\n </mat-form-field>\n </div>\n <div class=\"g g-auto-220 gap-12 ai-end mt-12\">\n <mat-form-field appearance=\"outline\" class=\"col-span-2\">\n <mat-label>{{ tx('Included fields') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getExportIncludeFieldsText()\"\n (ngModelChange)=\"onExportIncludeFieldsTextChange($event)\"\n [placeholder]=\"tx('Empty means all fields')\"\n />\n @if (getExportIncludeFieldsWarning(); as warning) {\n <mat-hint>{{ warning }}</mat-hint>\n }\n </mat-form-field>\n <mat-slide-toggle\n [ngModel]=\"getExportIncludeHeadersModel()\"\n (ngModelChange)=\"onExportIncludeHeadersChange($event)\"\n >{{ tx('Include headers') }}</mat-slide-toggle\n >\n <mat-slide-toggle\n [ngModel]=\"getExportApplyFormattingModel()\"\n (ngModelChange)=\"onExportApplyFormattingChange($event)\"\n >{{ tx('Apply formatting') }}</mat-slide-toggle\n >\n </div>\n <div class=\"grid gap-2 mt-12\">\n <div class=\"subtitle\">{{ tx('Export preview') }}</div>\n <div class=\"muted\">{{ getExportPreviewSummary() }}</div>\n @if (getExportPreviewColumns().length) {\n <div class=\"muted\">\n {{ tx('Columns') }}:\n {{ getExportPreviewColumns().join(', ') }}\n </div>\n @for (row of getExportPreviewRows(); track $index) {\n <div class=\"muted\">\n @for (column of getExportPreviewColumns(); track column) {\n <span>{{ column }}={{ row[column] }}</span>\n }\n </div>\n }\n } @else {\n <div class=\"muted\">\n {{ tx('No fields available for preview yet.') }}\n </div>\n }\n </div>\n </mat-expansion-panel>\n }\n @if (working.ui?.showSearch) {\n <div\n class=\"g g-auto-220 gap-12 ai-end mt-12\"\n >\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Field to search') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.ui.searchField\"\n (ngModelChange)=\"onUiChanged()\"\n >\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Search placeholder') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.ui.searchPlaceholder\"\n (ngModelChange)=\"onUiChanged()\"\n [placeholder]=\"tx('e.g.: Search by title')\"\n />\n </mat-form-field>\n </div>\n }\n @if (working.ui?.showSort) {\n <div class=\"mt-12\">\n <div class=\"g g-1-auto ai-center gap-8\">\n <div class=\"muted\">\n {{ tx('Sorting options (label \u2192 field+direction)') }}\n </div>\n <button mat-flat-button color=\"primary\" (click)=\"addUiSortRow()\">\n {{ tx('Add option') }}\n </button>\n </div>\n @for (r of uiSortRows; track $index; let i = $index) {\n <div\n class=\"g g-auto-220 gap-12 ai-end mt-12\"\n >\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Label') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"r.label\"\n (ngModelChange)=\"onUiSortRowsChanged()\"\n [placeholder]=\"tx('e.g.: Most recent')\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Field') }}</mat-label>\n <mat-select\n [(ngModel)]=\"r.field\"\n (ngModelChange)=\"onUiSortRowsChanged()\"\n >\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Direction') }}</mat-label>\n <mat-select\n [(ngModel)]=\"r.dir\"\n (ngModelChange)=\"onUiSortRowsChanged()\"\n >\n <mat-option value=\"desc\">{{ tx('Descending') }}</mat-option>\n <mat-option value=\"asc\">{{ tx('Ascending') }}</mat-option>\n </mat-select>\n </mat-form-field>\n @if (isUiSortRowDuplicate(i)) {\n <div class=\"error\">\n {{ tx('Duplicate option (field+direction)') }}\n </div>\n }\n <div class=\"flex-end\">\n <button mat-button color=\"warn\" (click)=\"removeUiSortRow(i)\">\n {{ tx('Remove') }}\n </button>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </ng-template>\n </mat-tab>\n <mat-tab [label]=\"tx('Content')\">\n <ng-template matTabContent>\n <div class=\"editor-content\">\n <div class=\"editor-main\">\n <mat-accordion multi>\n <!-- Primary -->\n <mat-expansion-panel [expanded]=\"true\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"g row-flow gap-8 ai-center\">\n <mat-icon>{{ getTypeIcon(mappingPrimary.type) }}</mat-icon>\n <span>{{ tx('Primary (Title)') }}</span>\n </div>\n </mat-panel-title>\n <mat-panel-description>{{\n mappingPrimary.field || tx('Not mapped')\n }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g gap-12\">\n <div class=\"g row-flow gap-8\">\n <span class=\"text-caption muted\">{{ tx('Presets') }}</span>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingPrimary.type = 'text';\n mappingPrimary.field = 'name';\n onMappingChanged()\n \"\n >\n {{ tx('Name') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingPrimary.type = 'text';\n mappingPrimary.field = 'title';\n onMappingChanged()\n \"\n >\n {{ tx('Title') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingPrimary.type = 'text';\n mappingPrimary.field = 'name';\n mappingSecondary.type = 'text';\n mappingSecondary.field = 'role';\n onMappingChanged()\n \"\n >\n {{ tx('Name + role') }}\n </button>\n </div>\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Field') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingPrimary.field\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingPrimary.type\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (mt of primaryTypeConfigs; track mt.type) {\n <mat-option\n [value]=\"mt.type\"\n >\n <mat-icon class=\"option-icon\">{{ mt.icon }}</mat-icon>\n {{ tx(mt.label) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n @switch (mappingPrimary.type) {\n @case (\"text\") {\n <praxis-meta-editor-text\n [model]=\"mappingPrimary\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"html\") {\n <praxis-meta-editor-text\n [model]=\"mappingPrimary\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"currency\") {\n <praxis-meta-editor-currency\n [model]=\"mappingPrimary\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-currency>\n }\n @case (\"date\") {\n <praxis-meta-editor-date\n [model]=\"mappingPrimary\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-date>\n }\n }\n\n <!-- Advanced -->\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ tx('Formatting and style') }}</mat-panel-title>\n </mat-expansion-panel-header>\n <div class=\"g gap-12 pt-12\">\n @if (\n mappingPrimary.type === 'text' ||\n mappingPrimary.type === 'html'\n ) {\n <mat-form-field\n appearance=\"outline\"\n >\n <mat-label>{{ tx('CSS class') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingPrimary.class\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n }\n @if (\n mappingPrimary.type === 'text' ||\n mappingPrimary.type === 'html'\n ) {\n <mat-form-field\n appearance=\"outline\"\n >\n <mat-label>{{ tx('Inline style') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingPrimary.style\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n }\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('CSS class') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingPrimary.class\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Inline style') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingPrimary.style\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n </div>\n </div>\n </mat-expansion-panel>\n </div>\n </mat-expansion-panel>\n\n <!-- Secondary -->\n <mat-expansion-panel [expanded]=\"!!mappingSecondary.field\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"g row-flow gap-8 ai-center\">\n <mat-icon>{{ getTypeIcon(mappingSecondary.type) }}</mat-icon>\n <span>{{ tx('Secondary (Summary)') }}</span>\n </div>\n </mat-panel-title>\n <mat-panel-description>{{\n mappingSecondary.field || tx('Not mapped')\n }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g gap-12\">\n <div class=\"g row-flow gap-8\">\n <span class=\"text-caption muted\">{{ tx('Presets') }}</span>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingSecondary.type = 'text';\n mappingSecondary.field = 'subtitle';\n onMappingChanged()\n \"\n >\n {{ tx('Subtitle') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingSecondary.type = 'date';\n mappingSecondary.field = 'hireDate';\n mappingSecondary.dateStyle = 'short';\n onMappingChanged()\n \"\n >\n {{ tx('Short date') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"applySalaryPreset()\"\n >\n {{ tx('Salary') }}\n </button>\n </div>\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Field') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingSecondary.field\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingSecondary.type\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (mt of secondaryTypeConfigs; track mt.type) {\n <mat-option\n [value]=\"mt.type\"\n >\n <mat-icon class=\"option-icon\">{{ mt.icon }}</mat-icon>\n {{ tx(mt.label) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n @switch (mappingSecondary.type) {\n @case (\"text\") {\n <praxis-meta-editor-text\n [model]=\"mappingSecondary\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"html\") {\n <praxis-meta-editor-text\n [model]=\"mappingSecondary\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"currency\") {\n <praxis-meta-editor-currency\n [model]=\"mappingSecondary\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-currency>\n }\n @case (\"date\") {\n <praxis-meta-editor-date\n [model]=\"mappingSecondary\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-date>\n }\n }\n\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ tx('Formatting and style') }}</mat-panel-title>\n </mat-expansion-panel-header>\n <div class=\"g gap-12 pt-12\">\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('CSS class') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingSecondary.class\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Inline style') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingSecondary.style\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n </div>\n </div>\n </mat-expansion-panel>\n </div>\n </mat-expansion-panel>\n\n <mat-expansion-panel\n [expanded]=\"!!mappingMeta.field || mappingMetaFields.length > 0\"\n >\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"g row-flow gap-8 ai-center\">\n <mat-icon>{{\n getTypeIcon(mappingMeta.type || \"text\")\n }}</mat-icon>\n <span>{{ tx('Meta (Detail/Side)') }}</span>\n </div>\n </mat-panel-title>\n <mat-panel-description>\n {{\n mappingMetaFields.length\n ? tx('Composed field ({{count}})', { count: mappingMetaFields.length })\n : mappingMeta.field || tx('Not mapped')\n }}\n </mat-panel-description>\n </mat-expansion-panel-header>\n\n <div class=\"g gap-12\">\n <!-- Composition Mode Toggle -->\n <div class=\"g g-1-1 gap-12 p-12 bg-subtle rounded\">\n <div class=\"text-caption muted\">{{ tx('Composition mode') }}</div>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Fields to compose (multi-select)') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingMetaFields\"\n multiple\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n @if (mappingMetaFields.length) {\n <div\n class=\"g g-1-1 ai-center gap-12\"\n >\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Separator') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingMetaSeparator\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n <mat-slide-toggle\n [(ngModel)]=\"mappingMetaWrapSecondInParens\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n {{ tx('(Second) in parentheses') }}\n </mat-slide-toggle>\n </div>\n }\n </div>\n\n <!-- Single Field Mode (if no composition) -->\n @if (!mappingMetaFields.length) {\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Single field') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingMeta.field\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n <mat-option [value]=\"undefined\">{{ tx('-- None --') }}</mat-option>\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingMeta.type\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (mt of metaTypeConfigs; track mt.type) {\n <mat-option\n [value]=\"mt.type\"\n >\n <mat-icon class=\"option-icon\">{{ mt.icon }}</mat-icon>\n {{ tx(mt.label) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n }\n\n <!-- Type configuration (pluggable editors) -->\n @switch (mappingMeta.type) {\n @case (\"text\") {\n <praxis-meta-editor-text\n [model]=\"mappingMeta\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"html\") {\n <praxis-meta-editor-text\n [model]=\"mappingMeta\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"currency\") {\n <praxis-meta-editor-currency\n [model]=\"mappingMeta\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-currency>\n }\n @case (\"date\") {\n <praxis-meta-editor-date\n [model]=\"mappingMeta\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-date>\n }\n @case (\"chip\") {\n <praxis-meta-editor-chip\n [model]=\"mappingMeta\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-chip>\n }\n @case (\"rating\") {\n <praxis-meta-editor-rating\n [model]=\"mappingMeta\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-rating>\n }\n @case (\"icon\") {\n <praxis-meta-editor-icon\n [model]=\"mappingMeta\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-icon>\n }\n @case (\"image\") {\n <praxis-meta-editor-image\n [model]=\"mappingMeta\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-image>\n }\n }\n\n <!-- Advanced -->\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header\n ><mat-panel-title>{{ tx('Advanced options') }}</mat-panel-title\n ></mat-expansion-panel-header\n >\n <div class=\"g gap-12 pt-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Position') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingMeta.placement\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n <mat-option value=\"side\">{{ tx('Side (right)') }}</mat-option>\n <mat-option value=\"line\">{{ tx('Inline (below)') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('CSS class') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingMeta.class\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Style') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingMeta.style\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n </div>\n </mat-expansion-panel>\n </div>\n </mat-expansion-panel>\n <!-- Trailing -->\n <mat-expansion-panel [expanded]=\"!!mappingTrailing.field\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"g row-flow gap-8 ai-center\">\n <mat-icon>{{\n getTypeIcon(mappingTrailing.type || \"text\")\n }}</mat-icon>\n <span>{{ tx('Trailing (right)') }}</span>\n </div>\n </mat-panel-title>\n <mat-panel-description>{{\n mappingTrailing.field || tx('Not mapped')\n }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g gap-12\">\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Field') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingTrailing.field\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n <mat-option [value]=\"undefined\">{{ tx('-- None --') }}</mat-option>\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingTrailing.type\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (mt of trailingTypeConfigs; track mt.type) {\n <mat-option\n [value]=\"mt.type\"\n >\n <mat-icon class=\"option-icon\">{{ mt.icon }}</mat-icon>\n {{ tx(mt.label) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"g row-flow gap-8\">\n <span class=\"text-caption muted\">{{ tx('Presets') }}</span>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingTrailing.type = 'chip';\n mappingTrailing.chipColor = 'primary';\n mappingTrailing.chipVariant = 'filled';\n mappingTrailing.field = 'status';\n onMappingChanged()\n \"\n >\n {{ tx('Status chip') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingTrailing.type = 'icon';\n mappingTrailing.field = 'status';\n mappingTrailing.iconColor = 'primary';\n onMappingChanged()\n \"\n >\n {{ tx('Status icon') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"applyPricePreset()\"\n >\n {{ tx('Price') }}\n </button>\n </div>\n\n @switch (mappingTrailing.type) {\n @case (\"text\") {\n <praxis-meta-editor-text\n [model]=\"mappingTrailing\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"html\") {\n <praxis-meta-editor-text\n [model]=\"mappingTrailing\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"currency\") {\n <praxis-meta-editor-currency\n [model]=\"mappingTrailing\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-currency>\n }\n @case (\"date\") {\n <praxis-meta-editor-date\n [model]=\"mappingTrailing\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-date>\n }\n @case (\"chip\") {\n <praxis-meta-editor-chip\n [model]=\"mappingTrailing\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-chip>\n }\n @case (\"rating\") {\n <praxis-meta-editor-rating\n [model]=\"mappingTrailing\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-rating>\n }\n @case (\"icon\") {\n <praxis-meta-editor-icon\n [model]=\"mappingTrailing\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-icon>\n }\n @case (\"image\") {\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('URL / Expr') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingTrailing.imageUrl\"\n (ngModelChange)=\"onMappingChanged()\"\n [placeholder]=\"tx('https://... or ${item.imageUrl}')\"\n />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"tx('Use an absolute/relative URL or a ${item.field} expression.')\"\n [attr.aria-label]=\"tx('Use an absolute/relative URL or a ${item.field} expression.')\"\n ></button>\n @if (\n isImageUrlRequiredInvalid(mappingTrailing.imageUrl)\n ) {\n <mat-error\n >{{ tx('URL/expr is required') }}</mat-error\n >\n }\n </mat-form-field>\n </div>\n <praxis-meta-editor-image\n [model]=\"mappingTrailing\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-image>\n @if (!mappingTrailing.imageUrl) {\n <div\n class=\"text-caption muted\"\n >\n {{ tx('Set the URL/expr to render the image.') }}\n </div>\n }\n }\n }\n\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header\n ><mat-panel-title>{{ tx('Style') }}</mat-panel-title\n ></mat-expansion-panel-header\n >\n <div class=\"g gap-12 pt-12\">\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Class') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingTrailing.class\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Style') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingTrailing.style\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n </div>\n </div>\n </mat-expansion-panel>\n </div>\n </mat-expansion-panel>\n\n <!-- Leading -->\n <mat-expansion-panel\n [expanded]=\"\n !!mappingLeading.field ||\n (mappingLeading.type === 'icon' && !!mappingLeading.icon) ||\n (mappingLeading.type === 'image' && !!mappingLeading.imageUrl)\n \"\n >\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"g row-flow gap-8 ai-center\">\n <mat-icon>{{ getTypeIcon(mappingLeading.type) }}</mat-icon>\n <span>{{ tx('Leading (left)') }}</span>\n </div>\n </mat-panel-title>\n <mat-panel-description>\n {{\n mappingLeading.type === \"icon\"\n ? mappingLeading.icon || tx('Static icon')\n : mappingLeading.field ||\n (mappingLeading.imageUrl\n ? tx('Static image')\n : tx('Not mapped'))\n }}\n </mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g gap-12\">\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingLeading.type\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (mt of leadingTypeConfigs; track mt.type) {\n <mat-option\n [value]=\"mt.type\"\n >\n <mat-icon class=\"option-icon\">{{ mt.icon }}</mat-icon>\n {{ tx(mt.label) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <!-- Field (only if not static icon/image, though user might want dynamic) -->\n @if (\n mappingLeading.type !== 'icon' &&\n mappingLeading.type !== 'image'\n ) {\n <mat-form-field\n appearance=\"outline\"\n >\n <mat-label>{{ tx('Field') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingLeading.field\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n </div>\n <div class=\"g row-flow gap-8\">\n <span class=\"text-caption muted\">{{ tx('Presets') }}</span>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingLeading.type = 'icon';\n mappingLeading.icon = 'person';\n mappingLeading.iconColor = 'primary';\n onMappingChanged()\n \"\n >\n {{ tx('Avatar icon') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingLeading.type = 'image';\n mappingLeading.imageUrl = 'https://placehold.co/64x64';\n mappingLeading.imageAlt = 'Avatar';\n mappingLeading.badgeText = '${item.status}';\n onMappingChanged()\n \"\n >\n {{ tx('Avatar image + badge') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingLeading.type = 'chip';\n mappingLeading.field = 'tag';\n mappingLeading.chipColor = 'accent';\n mappingLeading.chipVariant = 'filled';\n onMappingChanged()\n \"\n >\n {{ tx('Tag chip') }}\n </button>\n </div>\n\n <!-- Icon Specific -->\n @if (mappingLeading.type === 'icon') {\n <div\n class=\"g g-1-auto gap-12 ai-center\"\n >\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Icon') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingLeading.icon\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'search'\"\n size=\"dense\"\n [matTooltip]=\"tx('Choose icon')\"\n [attr.aria-label]=\"tx('Choose icon')\"\n (click)=\"pickLeadingIcon()\"\n ></button>\n </mat-form-field>\n <div class=\"text-caption muted\">\n {{ tx('Use the `|iconMap` pipe in the extra pipe for dynamic rendering.') }}\n </div>\n </div>\n }\n @if (mappingLeading.type === 'icon') {\n <div>\n <praxis-meta-editor-icon\n [model]=\"mappingLeading\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground\"\n [isCustomColor]=\"isCustomColor\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-icon>\n </div>\n }\n\n <!-- Image Specific -->\n @if (mappingLeading.type === 'image') {\n <div\n class=\"g g-1-1 gap-12\"\n >\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Image URL') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingLeading.imageUrl\"\n (ngModelChange)=\"onMappingChanged()\"\n [placeholder]=\"tx('https://... or ${item.imageUrl}')\"\n />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"tx('Use an absolute/relative URL or a ${item.field} expression.')\"\n [attr.aria-label]=\"tx('Use an absolute/relative URL or a ${item.field} expression.')\"\n ></button>\n @if (isImageUrlRequiredInvalid(mappingLeading.imageUrl)) {\n <mat-error\n >{{ tx('URL/expr is required') }}</mat-error\n >\n }\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Alt text') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingLeading.imageAlt\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Badge text') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingLeading.badgeText\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n </div>\n }\n\n @switch (mappingLeading.type) {\n @case (\"text\") {\n <praxis-meta-editor-text\n [model]=\"mappingLeading\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"html\") {\n <praxis-meta-editor-text\n [model]=\"mappingLeading\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"chip\") {\n <praxis-meta-editor-chip\n [model]=\"mappingLeading\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-chip>\n }\n @case (\"rating\") {\n <praxis-meta-editor-rating\n [model]=\"mappingLeading\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-rating>\n }\n }\n\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header\n ><mat-panel-title>{{ tx('Style') }}</mat-panel-title\n ></mat-expansion-panel-header\n >\n <div class=\"g gap-12 pt-12\">\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Class') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingLeading.class\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Style') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingLeading.style\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n </div>\n </mat-expansion-panel>\n </div>\n </mat-expansion-panel>\n\n <!-- Features -->\n <mat-expansion-panel\n [expanded]=\"featuresVisible && features.length > 0\"\n >\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"g row-flow gap-8 ai-center\">\n <mat-icon>view_list</mat-icon>\n <span>{{ tx('Features') }}</span>\n </div>\n </mat-panel-title>\n <mat-panel-description\n >{{ features.length }} item(s)</mat-panel-description\n >\n </mat-expansion-panel-header>\n\n <div class=\"g gap-12\">\n <div class=\"g row-flow gap-12 ai-center\">\n <mat-slide-toggle\n [(ngModel)]=\"featuresVisible\"\n (ngModelChange)=\"onFeaturesChanged()\"\n >{{ tx('Enable features') }}</mat-slide-toggle\n >\n <mat-slide-toggle\n [(ngModel)]=\"featuresSyncWithMeta\"\n (ngModelChange)=\"onMappingChanged()\"\n >{{ tx('Sync with Meta') }}</mat-slide-toggle\n >\n <span class=\"flex-1\"></span>\n <mat-button-toggle-group\n [(ngModel)]=\"featuresMode\"\n (change)=\"onFeaturesChanged()\"\n appearance=\"legacy\"\n >\n <mat-button-toggle value=\"icons+labels\"\n ><mat-icon>view_list</mat-icon></mat-button-toggle\n >\n <mat-button-toggle value=\"icons-only\"\n ><mat-icon>more_horiz</mat-icon></mat-button-toggle\n >\n </mat-button-toggle-group>\n </div>\n\n @for (f of features; track $index; let i = $index) {\n <div\n class=\"g g-auto-1 gap-8 ai-center p-8 border rounded mb-2\"\n >\n <button\n type=\"button\"\n [praxisIconButton]=\"f.icon || 'search'\"\n size=\"dense\"\n [matTooltip]=\"tx('Choose feature icon')\"\n [attr.aria-label]=\"tx('Choose feature icon')\"\n (click)=\"pickFeatureIcon(i)\"\n ></button>\n <mat-form-field\n appearance=\"outline\"\n class=\"dense-form-field no-sub\"\n >\n <input\n matInput\n [(ngModel)]=\"f.expr\"\n (ngModelChange)=\"onFeaturesChanged()\"\n [placeholder]=\"tx('Expression/Text')\"\n />\n </mat-form-field>\n <button\n type=\"button\"\n [praxisIconButton]=\"'delete'\"\n size=\"dense\"\n class=\"editor-icon-action--danger\"\n [matTooltip]=\"tx('Remove feature')\"\n [attr.aria-label]=\"tx('Remove feature')\"\n (click)=\"removeFeature(i)\"\n ></button>\n </div>\n }\n <button mat-button color=\"primary\" (click)=\"addFeature()\">\n <mat-icon>add</mat-icon> {{ tx('Add feature') }}\n </button>\n </div>\n </mat-expansion-panel>\n <!-- Section Header -->\n <mat-expansion-panel [expanded]=\"!!mappingSectionHeader.expr\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"g row-flow gap-8 ai-center\">\n <mat-icon>{{\n getTypeIcon(mappingSectionHeader.type)\n }}</mat-icon>\n <span>{{ tx('Section header') }}</span>\n </div>\n </mat-panel-title>\n <mat-panel-description>{{\n mappingSectionHeader.expr || tx('Not configured')\n }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g gap-12\">\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingSectionHeader.type\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (mt of sectionHeaderTypeConfigs; track mt.type) {\n <mat-option\n [value]=\"mt.type\"\n >\n <mat-icon class=\"option-icon\">{{ mt.icon }}</mat-icon>\n {{ tx(mt.label) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Expression (item.key)') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingSectionHeader.expr\"\n (ngModelChange)=\"onMappingChanged()\"\n placeholder=\"item.key\"\n />\n </mat-form-field>\n </div>\n <div class=\"g row-flow gap-8\">\n <span class=\"text-caption muted\">{{ tx('Presets') }}</span>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingSectionHeader.type = 'text';\n mappingSectionHeader.expr = '${item.key}';\n onMappingChanged()\n \"\n >\n {{ tx('Default text') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingSectionHeader.type = 'chip';\n mappingSectionHeader.chipColor = 'primary';\n mappingSectionHeader.chipVariant = 'filled';\n mappingSectionHeader.expr = '${item.key}';\n onMappingChanged()\n \"\n >\n {{ tx('Default chip') }}\n </button>\n </div>\n\n @switch (mappingSectionHeader.type) {\n @case (\"text\") {\n <praxis-meta-editor-text\n [model]=\"mappingSectionHeader\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"html\") {\n <praxis-meta-editor-text\n [model]=\"mappingSectionHeader\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"chip\") {\n <praxis-meta-editor-chip\n [model]=\"mappingSectionHeader\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-chip>\n }\n @case (\"rating\") {\n <praxis-meta-editor-rating\n [model]=\"mappingSectionHeader\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-rating>\n }\n @case (\"icon\") {\n <praxis-meta-editor-icon\n [model]=\"mappingSectionHeader\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-icon>\n }\n @case (\"image\") {\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Image URL') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingSectionHeader.imageUrl\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n @if (\n isImageUrlRequiredInvalid(\n mappingSectionHeader.imageUrl\n )\n ) {\n <mat-error\n >{{ tx('URL/expr is required') }}</mat-error\n >\n }\n </mat-form-field>\n </div>\n @if (!mappingSectionHeader.imageUrl) {\n <div\n class=\"text-caption muted\"\n >\n {{ tx('Set the URL/expr to render the image.') }}\n </div>\n }\n <praxis-meta-editor-image\n [model]=\"mappingSectionHeader\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-image>\n }\n }\n\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header\n ><mat-panel-title>{{ tx('Style') }}</mat-panel-title\n ></mat-expansion-panel-header\n >\n <div class=\"g gap-12 pt-12\">\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Class') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingSectionHeader.class\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Style') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingSectionHeader.style\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n </div>\n </mat-expansion-panel>\n </div>\n </mat-expansion-panel>\n\n <!-- Empty State -->\n <mat-expansion-panel [expanded]=\"!!mappingEmptyState.expr\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"g row-flow gap-8 ai-center\">\n <mat-icon>inbox</mat-icon>\n <span>{{ tx('Empty state') }}</span>\n </div>\n </mat-panel-title>\n <mat-panel-description>{{\n mappingEmptyState.expr || tx('Default')\n }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g gap-12\">\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingEmptyState.type\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (mt of emptyStateTypeConfigs; track mt.type) {\n <mat-option\n [value]=\"mt.type\"\n >\n <mat-icon class=\"option-icon\">{{ mt.icon }}</mat-icon>\n {{ tx(mt.label) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Message / Expr') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingEmptyState.expr\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n </div>\n <div class=\"g row-flow gap-8\">\n <span class=\"text-caption muted\">{{ tx('Presets') }}</span>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingEmptyState.type = 'text';\n mappingEmptyState.expr = tx('No items available');\n onMappingChanged()\n \"\n >\n {{ tx('Default message') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingEmptyState.type = 'image';\n mappingEmptyState.imageUrl = '/list-empty-state.svg';\n mappingEmptyState.imageAlt = tx('No results');\n onMappingChanged()\n \"\n >\n {{ tx('Default image') }}\n </button>\n </div>\n\n @switch (mappingEmptyState.type) {\n @case (\"text\") {\n <praxis-meta-editor-text\n [model]=\"mappingEmptyState\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"html\") {\n <praxis-meta-editor-text\n [model]=\"mappingEmptyState\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"chip\") {\n <praxis-meta-editor-chip\n [model]=\"mappingEmptyState\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-chip>\n }\n @case (\"rating\") {\n <praxis-meta-editor-rating\n [model]=\"mappingEmptyState\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground\"\n [isCustomColor]=\"isCustomColor\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-rating>\n }\n @case (\"icon\") {\n <praxis-meta-editor-icon\n [model]=\"mappingEmptyState\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground\"\n [isCustomColor]=\"isCustomColor\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-icon>\n }\n @case (\"image\") {\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Image URL') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingEmptyState.imageUrl\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n @if (\n isImageUrlRequiredInvalid(mappingEmptyState.imageUrl)\n ) {\n <mat-error\n >{{ tx('URL/expr is required') }}</mat-error\n >\n }\n </mat-form-field>\n </div>\n @if (!mappingEmptyState.imageUrl) {\n <div\n class=\"text-caption muted\"\n >\n {{ tx('Set the URL/expr to render the image.') }}\n </div>\n }\n <praxis-meta-editor-image\n [model]=\"mappingEmptyState\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-image>\n }\n }\n\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header\n ><mat-panel-title>{{ tx('Style') }}</mat-panel-title\n ></mat-expansion-panel-header\n >\n <div class=\"g gap-12 pt-12\">\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Class') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingEmptyState.class\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Style') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingEmptyState.style\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n </div>\n </mat-expansion-panel>\n </div>\n </mat-expansion-panel>\n </mat-accordion>\n\n <button mat-flat-button color=\"primary\" (click)=\"applyTemplate()\">\n {{ tx('Apply mapping') }}\n </button>\n <button\n mat-button\n (click)=\"inferFromFields()\"\n [disabled]=\"!fields.length\"\n >\n {{ tx('Infer from schema') }}\n </button>\n <div class=\"g g-auto-220 gap-12 ai-end mt-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Skeleton count') }}</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n [(ngModel)]=\"skeletonCountInput\"\n (ngModelChange)=\"onSkeletonChanged($event)\"\n />\n </mat-form-field>\n </div>\n\n <div class=\"g gap-12 mt-12\">\n <div class=\"g row-flow gap-8 ai-center\">\n <span class=\"section-title mat-subtitle-1\">{{ tx('Theme preview') }}</span>\n <mat-button-toggle-group\n [(ngModel)]=\"skinPreviewTheme\"\n (change)=\"onSkinChanged()\"\n appearance=\"legacy\"\n >\n <mat-button-toggle [value]=\"'light'\">{{ tx('Light') }}</mat-button-toggle>\n <mat-button-toggle [value]=\"'dark'\">{{ tx('Dark') }}</mat-button-toggle>\n <mat-button-toggle [value]=\"'grid'\">{{ tx('Grid') }}</mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n <div class=\"skin-preview-wrap\">\n <praxis-list-skin-preview\n [config]=\"working\"\n [items]=\"previewData\"\n [theme]=\"skinPreviewTheme\"\n ></praxis-list-skin-preview>\n </div>\n </div>\n </div>\n </div>\n </ng-template>\n </mat-tab>\n <mat-tab [label]=\"tx('Rules')\">\n <ng-template matTabContent>\n <div class=\"editor-content grid gap-3\">\n <div class=\"g g-1-auto ai-center gap-8\">\n <div>\n <div class=\"subtitle\">{{ tx('Item style rules') }}</div>\n <div class=\"muted\">\n {{ tx('Apply class, style, border or background when Json Logic matches.') }}\n </div>\n </div>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n (click)=\"addItemStyleRule()\"\n >\n {{ tx('Add item rule') }}\n </button>\n </div>\n @for (\n rule of working.rules?.itemStyles || [];\n track rule.id;\n let i = $index\n ) {\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\" [expanded]=\"i === 0\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ rule.id || tx('Item rule') }}</mat-panel-title>\n <mat-panel-description>{{ rule.class || rule.background || rule.border }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Id') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"rule.id\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"col-span-2\">\n <mat-label>{{ tx('Condition (Json Logic)') }}</mat-label>\n <textarea\n matInput\n rows=\"3\"\n [ngModel]=\"getRuleConditionText(rule)\"\n (ngModelChange)=\"onRuleConditionChanged(rule, $event)\"\n ></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('CSS class') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"rule.class\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Inline style') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"rule.style\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Border') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"rule.border\"\n (ngModelChange)=\"onRulesChanged()\"\n placeholder=\"1px solid var(--md-sys-color-outline)\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Background') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"rule.background\"\n (ngModelChange)=\"onRulesChanged()\"\n placeholder=\"var(--md-sys-color-surface-container)\"\n />\n </mat-form-field>\n <div class=\"flex-end\">\n <button\n mat-button\n color=\"warn\"\n type=\"button\"\n (click)=\"removeItemStyleRule(i)\"\n >\n {{ tx('Remove') }}\n </button>\n </div>\n <div class=\"col-span-2\">\n <div class=\"g g-1-auto ai-center gap-8\">\n <div>\n <div class=\"subtitle\">{{ tx('Canonical effect') }}</div>\n <div class=\"muted\">\n {{ tx('Materialize the first effects[] entry used by the runtime.') }}\n </div>\n </div>\n @if (getItemStyleEffect(rule)) {\n <button\n mat-button\n type=\"button\"\n (click)=\"clearItemStyleEffects(rule)\"\n >\n {{ tx('Use direct fields') }}\n </button>\n } @else {\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"ensureItemStyleEffect(rule)\"\n >\n {{ tx('Use effects[]') }}\n </button>\n }\n </div>\n </div>\n @if (getItemStyleEffect(rule); as effect) {\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Effect class') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"effect.class\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Effect style') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"effect.style\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Effect border') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"effect.border\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Effect background') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"effect.background\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n }\n </div>\n </mat-expansion-panel>\n }\n\n <mat-divider class=\"my-8\"></mat-divider>\n\n <div class=\"g g-1-auto ai-center gap-8\">\n <div>\n <div class=\"subtitle\">{{ tx('Slot override rules') }}</div>\n <div class=\"muted\">\n {{ tx('Replace, decorate or hide a slot when Json Logic matches.') }}\n </div>\n </div>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n (click)=\"addSlotOverrideRule()\"\n >\n {{ tx('Add slot rule') }}\n </button>\n </div>\n @for (\n rule of working.rules?.slotOverrides || [];\n track rule.id;\n let i = $index\n ) {\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\" [expanded]=\"i === 0\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ rule.id || tx('Slot rule') }}</mat-panel-title>\n <mat-panel-description>{{ rule.slot }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Id') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"rule.id\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Slot') }}</mat-label>\n <mat-select\n [(ngModel)]=\"rule.slot\"\n (ngModelChange)=\"onRulesChanged()\"\n >\n @for (slot of ruleSlotOptions; track slot) {\n <mat-option [value]=\"slot\">{{ slot }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"col-span-2\">\n <mat-label>{{ tx('Condition (Json Logic)') }}</mat-label>\n <textarea\n matInput\n rows=\"3\"\n [ngModel]=\"getRuleConditionText(rule)\"\n (ngModelChange)=\"onRuleConditionChanged(rule, $event)\"\n ></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Template type') }}</mat-label>\n <mat-select\n [ngModel]=\"getSlotOverrideTemplateType(rule)\"\n (ngModelChange)=\"onSlotOverrideTemplateTypeChanged(rule, $event)\"\n >\n @for (type of ruleTemplateTypeOptions; track type) {\n <mat-option [value]=\"type\">{{ tx(type) }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Template expression') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getSlotOverrideTemplateExpr(rule)\"\n (ngModelChange)=\"onSlotOverrideTemplateExprChanged(rule, $event)\"\n placeholder=\"${item.status}\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('CSS class') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"rule.class\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Inline style') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"rule.style\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-slide-toggle\n [(ngModel)]=\"rule.hide\"\n (ngModelChange)=\"onRulesChanged()\"\n >{{ tx('Hide slot') }}</mat-slide-toggle>\n <div class=\"flex-end\">\n <button\n mat-button\n color=\"warn\"\n type=\"button\"\n (click)=\"removeSlotOverrideRule(i)\"\n >\n {{ tx('Remove') }}\n </button>\n </div>\n <div class=\"col-span-2\">\n <div class=\"g g-1-auto ai-center gap-8\">\n <div>\n <div class=\"subtitle\">{{ tx('Canonical effect') }}</div>\n <div class=\"muted\">\n {{ tx('Materialize the first effects[] entry used by the runtime.') }}\n </div>\n </div>\n @if (getSlotOverrideEffect(rule)) {\n <button\n mat-button\n type=\"button\"\n (click)=\"clearSlotOverrideEffects(rule)\"\n >\n {{ tx('Use direct fields') }}\n </button>\n } @else {\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"ensureSlotOverrideEffect(rule)\"\n >\n {{ tx('Use effects[]') }}\n </button>\n }\n </div>\n </div>\n @if (getSlotOverrideEffect(rule); as effect) {\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Effect template type') }}</mat-label>\n <mat-select\n [ngModel]=\"getSlotOverrideTemplateType(effect)\"\n (ngModelChange)=\"onSlotOverrideTemplateTypeChanged(effect, $event)\"\n >\n @for (type of ruleTemplateTypeOptions; track type) {\n <mat-option [value]=\"type\">{{ tx(type) }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Effect template expression') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getSlotOverrideTemplateExpr(effect)\"\n (ngModelChange)=\"onSlotOverrideTemplateExprChanged(effect, $event)\"\n placeholder=\"${item.status}\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Effect class') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"effect.class\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Effect style') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"effect.style\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-slide-toggle\n [(ngModel)]=\"effect.hide\"\n (ngModelChange)=\"onRulesChanged()\"\n >{{ tx('Effect hides slot') }}</mat-slide-toggle>\n }\n </div>\n </mat-expansion-panel>\n }\n </div>\n </ng-template>\n </mat-tab>\n <mat-tab [label]=\"tx('i18n/A11y')\">\n <ng-template matTabContent>\n @if (working?.a11y && working?.events) {\n <div\n class=\"editor-content grid gap-3\"\n >\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Default locale') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.i18n.locale\"\n (ngModelChange)=\"markDirty()\"\n [placeholder]=\"tx('e.g.: en-US')\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Default currency') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.i18n.currency\"\n (ngModelChange)=\"markDirty()\"\n [placeholder]=\"currencyPlaceholder()\"\n />\n </mat-form-field>\n <mat-divider class=\"my-8\"></mat-divider>\n <div class=\"subtitle\">{{ tx('Accessibility') }}</div>\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('aria-label') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working!.a11y!.ariaLabel\"\n (ngModelChange)=\"markDirty()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('aria-labelledby') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working!.a11y!.ariaLabelledBy\"\n (ngModelChange)=\"markDirty()\"\n />\n </mat-form-field>\n </div>\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-slide-toggle\n [(ngModel)]=\"working!.a11y!.highContrast\"\n (ngModelChange)=\"markDirty()\"\n >{{ tx('High contrast') }}</mat-slide-toggle\n >\n <mat-slide-toggle\n [(ngModel)]=\"working!.a11y!.reduceMotion\"\n (ngModelChange)=\"markDirty()\"\n >{{ tx('Reduce motion') }}</mat-slide-toggle\n >\n </div>\n <mat-divider class=\"my-8\"></mat-divider>\n <div class=\"subtitle\">{{ tx('Events') }}</div>\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('itemClick') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working!.events!.itemClick\"\n (ngModelChange)=\"markDirty()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>actionClick</mat-label>\n <input\n matInput\n [(ngModel)]=\"working!.events!.actionClick\"\n (ngModelChange)=\"markDirty()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>selectionChange</mat-label>\n <input\n matInput\n [(ngModel)]=\"working!.events!.selectionChange\"\n (ngModelChange)=\"markDirty()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>exportAction</mat-label>\n <input\n matInput\n [(ngModel)]=\"working!.events!.exportAction\"\n (ngModelChange)=\"markDirty()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>loaded</mat-label>\n <input\n matInput\n [(ngModel)]=\"working!.events!.loaded\"\n (ngModelChange)=\"markDirty()\"\n />\n </mat-form-field>\n </div>\n </div>\n }\n </ng-template>\n </mat-tab>\n <mat-tab [label]=\"tx('Selection')\">\n <ng-template matTabContent>\n <div class=\"editor-content grid gap-3\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Mode') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.selection.mode\"\n (ngModelChange)=\"onSelectionChanged()\"\n >\n <mat-option value=\"none\">{{ tx('No selection') }}</mat-option>\n <mat-option value=\"single\">{{ tx('Single') }}</mat-option>\n <mat-option value=\"multiple\">{{ tx('Multiple') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Form name') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.selection.formControlName\"\n (ngModelChange)=\"onSelectionChanged()\"\n />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n matTooltip=\"formControlName\"\n aria-label=\"formControlName\"\n ></button>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Form path') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.selection.formControlPath\"\n (ngModelChange)=\"onSelectionChanged()\"\n />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n matTooltip=\"formControlPath\"\n aria-label=\"formControlPath\"\n ></button>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Compare by (field)') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.selection.compareBy\"\n (ngModelChange)=\"onSelectionChanged()\"\n />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"tx('Unique item key.')\"\n [attr.aria-label]=\"tx('Unique item key.')\"\n ></button>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Return') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.selection.return\"\n (ngModelChange)=\"onSelectionChanged()\"\n >\n <mat-option value=\"value\">{{ tx('Value') }}</mat-option>\n <mat-option value=\"item\">{{ tx('Item') }}</mat-option>\n <mat-option value=\"id\">{{ tx('ID') }}</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </ng-template>\n </mat-tab>\n <mat-tab [label]=\"tx('Appearance')\">\n <ng-template matTabContent>\n <div class=\"editor-content grid gap-3\">\n <div class=\"preset-row g row-flow gap-8\">\n <button mat-button (click)=\"applySkinPreset('pill-soft')\">\n {{ tx('Pill Soft') }}\n </button>\n <button mat-button (click)=\"applySkinPreset('gradient-tile')\">\n {{ tx('Gradient Tile') }}\n </button>\n <button mat-button (click)=\"applySkinPreset('glass')\">\n {{ tx('Glass') }}\n </button>\n <button mat-button (click)=\"applySkinPreset('elevated')\">\n {{ tx('Elevated') }}\n </button>\n <button mat-button (click)=\"applySkinPreset('outline')\">\n {{ tx('Outline') }}\n </button>\n <button mat-button (click)=\"applySkinPreset('flat')\">\n {{ tx('Flat') }}\n </button>\n <button mat-button (click)=\"applySkinPreset('neumorphism')\">\n {{ tx('Neumorphism') }}\n </button>\n </div>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Style') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.skin.type\"\n (ngModelChange)=\"onSkinTypeChanged($event)\"\n >\n <mat-option value=\"pill-soft\">{{ tx('Pill Soft') }}</mat-option>\n <mat-option value=\"gradient-tile\">{{ tx('Gradient Tile') }}</mat-option>\n <mat-option value=\"glass\">{{ tx('Glass') }}</mat-option>\n <mat-option value=\"elevated\">{{ tx('Elevated') }}</mat-option>\n <mat-option value=\"outline\">{{ tx('Outline') }}</mat-option>\n <mat-option value=\"flat\">{{ tx('Flat') }}</mat-option>\n <mat-option value=\"neumorphism\">{{ tx('Neumorphism') }}</mat-option>\n <mat-option value=\"custom\">{{ tx('Custom') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Radius') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.skin.radius\"\n (ngModelChange)=\"onSkinChanged()\"\n [placeholder]=\"tx('e.g.: 1.25rem')\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Shadow') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.skin.shadow\"\n (ngModelChange)=\"onSkinChanged()\"\n [placeholder]=\"tx('e.g.: var(--md-sys-elevation-level2)')\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Border') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.skin.border\"\n (ngModelChange)=\"onSkinChanged()\"\n />\n </mat-form-field>\n @if (working.skin.type === 'glass') {\n <mat-form-field\n appearance=\"outline\"\n >\n <mat-label>{{ tx('Blur') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.skin.backdropBlur\"\n (ngModelChange)=\"onSkinChanged()\"\n [placeholder]=\"tx('e.g.: 8px')\"\n />\n </mat-form-field>\n }\n @if (working.skin.type === 'gradient-tile') {\n <div class=\"g gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Gradient from') }}</mat-label>\n <input\n matInput\n [ngModel]=\"working.skin.gradient.from || ''\"\n (ngModelChange)=\"onSkinGradientChanged('from', $event)\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Gradient to') }}</mat-label>\n <input\n matInput\n [ngModel]=\"working.skin.gradient.to || ''\"\n (ngModelChange)=\"onSkinGradientChanged('to', $event)\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Angle') }}</mat-label>\n <input\n matInput\n type=\"number\"\n [ngModel]=\"working.skin.gradient.angle ?? 135\"\n (ngModelChange)=\"onSkinGradientChanged('angle', $event)\"\n />\n </mat-form-field>\n </div>\n }\n\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Extra CSS class (skin.class)') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.skin.class\"\n (ngModelChange)=\"onSkinChanged()\"\n [placeholder]=\"tx('e.g.: my-list-skin')\"\n />\n </mat-form-field>\n\n @if (working.skin.type === 'custom') {\n <div\n class=\"g g-auto-220 gap-12 ai-end\"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>{{ tx('Inline style (skin.inlineStyle)') }}</mat-label>\n <textarea\n matInput\n rows=\"4\"\n [(ngModel)]=\"working.skin.inlineStyle\"\n (ngModelChange)=\"onSkinChanged()\"\n [attr.placeholder]=\"':host{--p-list-radius: 1rem}'\"\n ></textarea>\n </mat-form-field>\n <div class=\"text-caption\">\n {{ tx('CSS class example (add this to your global styles):') }}\n <pre class=\"code-block\">\n .my-list-skin .item-card {\n border-radius: 14px;\n border: 1px solid var(--md-sys-color-outline-variant);\n box-shadow: var(--md-sys-elevation-level2);\n }\n .my-list-skin .mat-mdc-list-item .list-item-content {\n backdrop-filter: blur(6px);\n }</pre\n >\n </div>\n </div>\n }\n </div>\n </ng-template>\n </mat-tab>\n </mat-tab-group>\n", styles: [".confirm-type{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;font-size:11px;line-height:16px;background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.confirm-type.danger{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.confirm-type.warning{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container)}.confirm-type.info{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}:host{display:block;color:var(--md-sys-color-on-surface)}.list-editor-tabs{--editor-surface: var(--md-sys-color-surface-container-lowest);--editor-border: 1px solid var(--md-sys-color-outline-variant);--editor-radius: var(--md-sys-shape-corner-large, 16px);--editor-muted: var(--md-sys-color-on-surface-variant);--editor-accent: var(--md-sys-color-primary)}.editor-content{padding:16px;background:var(--editor-surface);border:var(--editor-border);border-radius:var(--editor-radius);display:grid;gap:12px}.editor-content .mat-mdc-form-field{width:100%;max-width:none;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var( --md-sys-color-outline-variant );--mdc-outlined-text-field-hover-outline-color: var( --md-sys-color-outline );--mdc-outlined-text-field-focus-outline-color: var( --md-sys-color-primary );--mdc-outlined-text-field-error-outline-color: var( --md-sys-color-error );--mdc-outlined-text-field-error-focus-outline-color: var( --md-sys-color-error );--mdc-outlined-text-field-error-hover-outline-color: var( --md-sys-color-error );--mdc-outlined-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-outlined-text-field-input-text-color: var( --md-sys-color-on-surface );--mdc-outlined-text-field-supporting-text-color: var( --md-sys-color-on-surface-variant )}.editor-content .mat-mdc-form-field.w-full{max-width:none}.help-icon-button{vertical-align:middle}.editor-icon-action--danger{color:var(--md-sys-color-error)}.action-icon-preview{display:inline-grid;place-items:center;width:36px;height:36px;color:var(--md-sys-color-on-surface-variant)}.action-icon-preview mat-icon{width:18px;height:18px;font-size:18px;line-height:18px}.editor-split{grid-template-columns:minmax(0,1fr);align-items:start}.editor-main,.editor-aside{display:grid;gap:12px}.skin-preview-wrap{border-radius:calc(var(--editor-radius) - 4px);border:var(--editor-border);background:var(--md-sys-color-surface-container);padding:12px}.g{display:grid}.g-auto-220{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.g-auto-200{grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}.g-1-auto{grid-template-columns:1fr auto}.row-flow{grid-auto-flow:column}.gap-6{gap:6px}.gap-8{gap:8px}.gap-12{gap:12px}.ai-center{align-items:center}.ai-end{align-items:end}.mt-12{margin-top:12px}.mb-8{margin-bottom:8px}.mb-6{margin-bottom:6px}.my-8{margin:8px 0}.subtitle{margin:8px 0 4px;color:var(--editor-muted);font-weight:500}.section-title{color:var(--editor-muted);font-weight:600}.chips-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.error{color:var(--md-sys-color-error);font-size:.85rem}.muted{color:var(--editor-muted)}.text-caption{color:var(--editor-muted);font-size:.8rem}:host ::ng-deep .mat-mdc-select-panel .option-icon{font-size:18px;margin-right:6px;vertical-align:middle}:host ::ng-deep .mat-mdc-select-panel .color-dot{width:10px;height:10px;border-radius:999px;display:inline-block;margin-right:6px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-outline)}:host ::ng-deep .mat-mdc-select-panel .color-primary{background:var(--md-sys-color-primary)}:host ::ng-deep .mat-mdc-select-panel .color-accent{background:var(--md-sys-color-tertiary)}:host ::ng-deep .mat-mdc-select-panel .color-warn{background:var(--md-sys-color-error)}:host ::ng-deep .mat-mdc-select-panel .color-default{background:var(--md-sys-color-outline)}@media(max-width:1024px){.editor-split{grid-template-columns:minmax(0,1fr)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i3$3.MatTabContent, selector: "[matTabContent]" }, { kind: "component", type: i3$3.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i3$3.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3$1.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: i4$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4$2.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: MatSlideToggleModule }, { kind: "component", type: i8.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: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i10.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i10.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i10.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i10.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i10.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i11.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i11.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i12.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i3.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "ngmodule", type: MatMenuModule }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "component", type: PraxisListSkinPreviewComponent, selector: "praxis-list-skin-preview", inputs: ["config", "items", "theme"] }, { kind: "component", type: PraxisMetaEditorTextComponent, selector: "praxis-meta-editor-text", inputs: ["model", "setPipe"], outputs: ["change"] }, { kind: "component", type: PraxisMetaEditorChipComponent, selector: "praxis-meta-editor-chip", inputs: ["model", "paletteOptions", "colorDotBackground", "isCustomColor", "enableCustomColor"], outputs: ["change"] }, { kind: "component", type: PraxisMetaEditorRatingComponent, selector: "praxis-meta-editor-rating", inputs: ["model", "paletteOptions", "colorDotBackground", "isCustomColor", "enableCustomColor"], outputs: ["change"] }, { kind: "component", type: PraxisMetaEditorCurrencyComponent, selector: "praxis-meta-editor-currency", inputs: ["model"], outputs: ["change"] }, { kind: "component", type: PraxisMetaEditorDateComponent, selector: "praxis-meta-editor-date", inputs: ["model"], outputs: ["change"] }, { kind: "component", type: PraxisMetaEditorIconComponent, selector: "praxis-meta-editor-icon", inputs: ["model", "paletteOptions", "colorDotBackground", "isCustomColor", "enableCustomColor"], outputs: ["change"] }, { kind: "component", type: PraxisMetaEditorImageComponent, selector: "praxis-meta-editor-image", inputs: ["model"], outputs: ["change"] }, { kind: "component", type: PraxisListJsonConfigEditorComponent, selector: "praxis-list-json-config-editor", inputs: ["document"], outputs: ["documentChange", "validationChange", "editorEvent"] }, { kind: "component", type: PdxColorPickerComponent, selector: "pdx-color-picker", inputs: ["actionsLayout", "activeView", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "clearButton", "disabledMode", "readonlyMode", "visible", "presentationMode", "fillMode", "format", "gradientSettings", "icon", "iconClass", "svgIcon", "paletteSettings", "popupSettings", "preview", "rounded", "size", "tabindex", "views", "maxRecent", "showRecent"], outputs: ["valueChange", "open", "close", "cancel", "activeViewChange", "activeColorClick", "focusEvent", "blurEvent"] }, { kind: "component", type: SurfaceOpenActionEditorComponent, selector: "praxis-surface-open-action-editor", inputs: ["value", "hostKind"], outputs: ["valueChange"] }] });
|
|
7411
|
+
], ngImport: i0, template: "<mat-tab-group class=\"list-editor-tabs\">\n <mat-tab [label]=\"tx('Data')\">\n <ng-template matTabContent>\n <div class=\"editor-content\">\n <div class=\"g g-1-auto gap-8 ai-center\">\n <div class=\"muted\">\n {{ tx('Assistant-applied adjustments replace the entire configuration object.') }}\n </div>\n <button\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"tx('applyConfigFromAdapter does not perform a deep merge. Make sure the adapter sends the full config.')\"\n [attr.aria-label]=\"tx('applyConfigFromAdapter does not perform a deep merge. Make sure the adapter sends the full config.')\"\n ></button>\n </div>\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>{{ tx('Resource (API)') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.dataSource.resourcePath\"\n (ngModelChange)=\"onResourcePathChange($event)\"\n [placeholder]=\"tx('e.g.: users')\"\n />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"tx('Resource endpoint (resourcePath).')\"\n [attr.aria-label]=\"tx('Resource endpoint (resourcePath).')\"\n ></button>\n </mat-form-field>\n <div class=\"grid gap-2 mb-12\">\n <div class=\"muted\">{{ getLocalDataSummary() }}</div>\n @if (getLocalDataPrecedenceWarning(); as warning) {\n <div class=\"muted\">{{ warning }}</div>\n }\n @if (getLocalDataColumns().length) {\n <div class=\"muted\">\n {{ tx('Local columns') }}:\n {{ getLocalDataColumns().join(', ') }}\n </div>\n @for (row of getLocalDataPreviewRows(); track $index) {\n <div class=\"muted\">\n @for (column of getLocalDataColumns().slice(0, 6); track column) {\n <span>{{ column }}={{ row[column] }}</span>\n }\n </div>\n }\n }\n </div>\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>{{ tx('Query (JSON)') }}</mat-label>\n <textarea\n matInput\n rows=\"3\"\n [(ngModel)]=\"queryJson\"\n (ngModelChange)=\"onQueryChanged($event)\"\n [placeholder]=\"queryPlaceholder()\"\n ></textarea>\n @if (!queryError) {\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"tx('Optional. Use valid JSON for initial filters.')\"\n [attr.aria-label]=\"tx('Optional. Use valid JSON for initial filters.')\"\n ></button>\n }\n @if (queryError) {\n <mat-error>{{ queryError }}</mat-error>\n }\n </mat-form-field>\n <div class=\"g g-auto-220 gap-12 ai-end mt-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Sort by') }}</mat-label>\n <mat-select\n [(ngModel)]=\"sortField\"\n (ngModelChange)=\"updateSortConfig()\"\n >\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"tx('Resource base field.')\"\n [attr.aria-label]=\"tx('Resource base field.')\"\n ></button>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Direction') }}</mat-label>\n <mat-select\n [(ngModel)]=\"sortDir\"\n (ngModelChange)=\"updateSortConfig()\"\n >\n <mat-option value=\"asc\">{{ tx('Ascending') }}</mat-option>\n <mat-option value=\"desc\">{{ tx('Descending') }}</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n </ng-template>\n </mat-tab>\n <mat-tab [label]=\"tx('JSON')\">\n <ng-template matTabContent>\n <div class=\"editor-content\">\n <praxis-list-json-config-editor\n [document]=\"document\"\n (documentChange)=\"onJsonConfigChange($event)\"\n (validationChange)=\"onJsonValidationChange($event)\"\n (editorEvent)=\"onJsonEditorEvent($event)\"\n >\n </praxis-list-json-config-editor>\n </div>\n </ng-template>\n </mat-tab>\n <mat-tab [label]=\"tx('Actions')\">\n <ng-template matTabContent>\n <div class=\"editor-content g gap-12\">\n <div class=\"g g-1-auto gap-8 ai-center\">\n <div class=\"muted\">\n {{ tx('Configure per-item action buttons (icon, label, color, visibility)') }}\n </div>\n <button mat-flat-button color=\"primary\" (click)=\"addAction()\">\n {{ tx('Add action') }}\n </button>\n </div>\n <div class=\"g g-1-auto gap-8 ai-center\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Global action (Praxis)') }}</mat-label>\n <mat-select\n [(ngModel)]=\"selectedGlobalActionId\"\n (ngModelChange)=\"onGlobalActionSelected($event)\"\n >\n <mat-option [value]=\"undefined\">{{ tx('-- Select --') }}</mat-option>\n @for (ga of globalActionCatalog; track ga.id) {\n <mat-option [value]=\"ga.id\">\n <mat-icon class=\"option-icon\">{{ ga.icon || \"bolt\" }}</mat-icon>\n {{ ga.label }}\n </mat-option>\n }\n </mat-select>\n @if (!globalActionCatalog.length) {\n <mat-hint\n class=\"text-caption muted\"\n >{{ tx('No global action registered.') }}</mat-hint\n >\n }\n </mat-form-field>\n <div class=\"muted text-caption\">\n {{ tx('Select to add with a structured global action.') }}\n </div>\n </div>\n @for (a of working.actions || []; track $index; let i = $index) {\n <div\n class=\"g g-auto-200 gap-12 ai-end\"\n >\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('ID') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"a.id\"\n (ngModelChange)=\"onActionsChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Action type') }}</mat-label>\n <mat-select [(ngModel)]=\"a.kind\" (ngModelChange)=\"onActionsChanged()\">\n <mat-option value=\"icon\">{{ tx('Icon') }}</mat-option>\n <mat-option value=\"button\">{{ tx('Button') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Icon') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"a.icon\"\n (ngModelChange)=\"onActionsChanged()\"\n [placeholder]=\"tx('e.g.: edit, delete')\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Global action') }}</mat-label>\n <mat-select\n [ngModel]=\"getGlobalActionId(a)\"\n (ngModelChange)=\"onActionGlobalActionIdChange(a, $event)\"\n >\n <mat-option value=\"\">{{ tx('None') }}</mat-option>\n @for (action of globalActionCatalog; track action.id) {\n <mat-option [value]=\"action.id\">\n {{ action.label || action.id }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Label') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"a.label\"\n (ngModelChange)=\"onActionsChanged()\"\n />\n </mat-form-field>\n @if (a.kind === 'button') {\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Variant') }}</mat-label>\n <mat-select\n [(ngModel)]=\"a.buttonVariant\"\n (ngModelChange)=\"onActionsChanged()\"\n >\n <mat-option value=\"stroked\">{{ tx('Outlined') }}</mat-option>\n <mat-option value=\"raised\">{{ tx('Raised') }}</mat-option>\n <mat-option value=\"flat\">{{ tx('Filled') }}</mat-option>\n </mat-select>\n </mat-form-field>\n }\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Action color') }}</mat-label>\n <mat-select\n [(ngModel)]=\"a.color\"\n (ngModelChange)=\"onActionsChanged()\"\n >\n @for (c of paletteOptions; track c.value || 'default') {\n <mat-option [value]=\"c.value\">\n <span\n class=\"color-dot\"\n [style.background]=\"colorDotBackground(c.value)\"\n ></span\n >{{ tx(c.label) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n @if (isCustomColor(a.color)) {\n <div\n class=\"g gap-8\"\n >\n <pdx-color-picker\n [label]=\"tx('Custom color')\"\n [format]=\"'hex'\"\n [(ngModel)]=\"a.color\"\n (ngModelChange)=\"onActionsChanged()\"\n ></pdx-color-picker>\n </div>\n } @else {\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"enableCustomActionColor(a)\"\n >\n {{ tx('Use custom color') }}\n </button>\n }\n <mat-form-field appearance=\"outline\" class=\"col-span-2\">\n <mat-label>{{ actionVisibilityLabel() }}</mat-label>\n <textarea\n matInput\n rows=\"3\"\n [ngModel]=\"actionShowIfModel(i)\"\n (ngModelChange)=\"onActionShowIfChanged(i, a, $event)\"\n ></textarea>\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"actionConditionTooltip()\"\n [attr.aria-label]=\"actionConditionTooltip()\"\n ></button>\n </mat-form-field>\n <div class=\"g row-flow gap-8 ai-center\">\n @if ((a.kind || 'icon') === 'icon') {\n <span class=\"action-icon-preview\" aria-hidden=\"true\">\n <mat-icon\n [praxisIcon]=\"a.icon || 'bolt'\"\n [style.cssText]=\"iconStyle(a.color)\"\n ></mat-icon>\n </span>\n }\n @if (a.kind === 'button') {\n @if (a.buttonVariant === 'stroked') {\n <button\n mat-stroked-button\n [color]=\"isThemeColor(a.color) ? a.color : undefined\"\n [style.cssText]=\"buttonStyle(a.color, 'stroked')\"\n >\n {{ a.label || a.id || tx('Action') }}\n </button>\n }\n @if (a.buttonVariant === 'raised') {\n <button\n mat-raised-button\n [color]=\"isThemeColor(a.color) ? a.color : undefined\"\n [style.cssText]=\"buttonStyle(a.color, 'raised')\"\n >\n {{ a.label || a.id || tx('Action') }}\n </button>\n }\n @if (!a.buttonVariant || a.buttonVariant === 'flat') {\n <button\n mat-flat-button\n [color]=\"isThemeColor(a.color) ? a.color : undefined\"\n [style.cssText]=\"buttonStyle(a.color, 'flat')\"\n >\n {{ a.label || a.id || tx('Action') }}\n </button>\n }\n }\n <span class=\"muted\">{{ tx('Preview') }}</span>\n </div>\n <div class=\"flex-end\">\n <button mat-button color=\"warn\" (click)=\"removeAction(i)\">\n {{ tx('Remove') }}\n </button>\n </div>\n <div class=\"g gap-8 col-span-2\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Placement') }}</mat-label>\n <mat-select\n [(ngModel)]=\"a.placement\"\n (ngModelChange)=\"onActionsChanged()\"\n >\n <mat-option [value]=\"undefined\">{{ tx('Default') }}</mat-option>\n <mat-option value=\"trailing\">{{ tx('Trailing') }}</mat-option>\n <mat-option value=\"actions\">{{ tx('Actions column') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-slide-toggle\n [(ngModel)]=\"a.showLoading\"\n (ngModelChange)=\"onActionsChanged()\"\n >{{ tx('Show loading') }}</mat-slide-toggle\n >\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header\n ><mat-panel-title\n >{{ tx('Confirmation') }}</mat-panel-title\n ></mat-expansion-panel-header\n >\n <div class=\"g gap-12 pt-12\">\n <div class=\"g row-flow gap-8 ai-center\">\n <span class=\"text-caption muted\">{{ tx('Type') }}</span>\n <mat-button-toggle-group\n [value]=\"a.confirmation?.type || ''\"\n (change)=\"applyConfirmationPreset(a, $event.value)\"\n >\n <mat-button-toggle value=\"\">{{ tx('Default') }}</mat-button-toggle>\n <mat-button-toggle value=\"danger\">{{ tx('Danger') }}</mat-button-toggle>\n <mat-button-toggle value=\"warning\">{{ tx('Warning') }}</mat-button-toggle>\n <mat-button-toggle value=\"info\">{{ tx('Info') }}</mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Title') }}</mat-label>\n <input\n matInput\n [ngModel]=\"a.confirmation?.title\"\n (ngModelChange)=\"setConfirmationField(a, 'title', $event)\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Message') }}</mat-label>\n <input\n matInput\n [ngModel]=\"a.confirmation?.message\"\n (ngModelChange)=\"setConfirmationField(a, 'message', $event)\"\n />\n </mat-form-field>\n <div class=\"g gap-6\">\n <div class=\"text-caption muted\">{{ tx('Preview') }}</div>\n <div class=\"text-caption\">\n <strong>{{\n a.confirmation?.title || tx('Confirm action')\n }}</strong>\n </div>\n <div class=\"text-caption muted\">\n {{\n a.confirmation?.message ||\n tx('Are you sure you want to continue?')\n }}\n </div>\n <div class=\"text-caption\">\n <span\n class=\"confirm-type\"\n [ngClass]=\"a.confirmation?.type || 'default'\"\n >{{ tx('Type') }}:\n {{ a.confirmation?.type || tx('Default').toLowerCase() }}</span\n >\n </div>\n @if (!a.confirmation?.title && !a.confirmation?.message) {\n <div\n class=\"text-caption muted\"\n >\n {{ tx('Set a title or message for the confirmation.') }}\n </div>\n }\n </div>\n </div>\n </mat-expansion-panel>\n @if (a.globalAction?.actionId) {\n @if (isSurfaceOpenCommand(a)) {\n <div class=\"col-span-2\">\n <praxis-surface-open-action-editor\n [value]=\"getSurfaceOpenGlobalActionPayload(a)\"\n hostKind=\"list\"\n (valueChange)=\"onSurfaceOpenGlobalActionPayloadChange(a, $event)\"\n ></praxis-surface-open-action-editor>\n </div>\n } @else {\n <mat-form-field appearance=\"outline\" class=\"col-span-2\">\n <mat-label>{{ tx('Payload (JSON/Template)') }}</mat-label>\n <textarea\n matInput\n rows=\"4\"\n [ngModel]=\"getGlobalActionPayloadText(a)\"\n (ngModelChange)=\"onGlobalActionPayloadTextChange(a, $event)\"\n placeholder='{\"message\":\"${item.name} favoritado\"}'\n ></textarea>\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"globalActionPayloadSchemaTooltip(a)\"\n [attr.aria-label]=\"globalActionPayloadSchemaTooltip(a)\"\n ></button>\n @if (isGlobalActionPayloadInvalid(a)) {\n <mat-error\n >{{ tx('Invalid JSON') }}</mat-error\n >\n }\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"col-span-2\">\n <mat-label>{{ tx('Payload expression') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getGlobalActionPayloadExprText(a)\"\n (ngModelChange)=\"onGlobalActionPayloadExprTextChange(a, $event)\"\n placeholder=\"item.warningMessage\"\n />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"tx('Advanced escape hatch. When set, payload is resolved from this expression and no default item payload is injected.')\"\n [attr.aria-label]=\"tx('Advanced escape hatch. When set, payload is resolved from this expression and no default item payload is injected.')\"\n ></button>\n </mat-form-field>\n <div class=\"g row-flow gap-8 ai-center\">\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"applyGlobalActionPayloadExample(a)\"\n >\n {{ tx('Insert example') }}\n </button>\n <span class=\"muted text-caption\">{{\n globalActionPayloadExampleHint(a)\n }}</span>\n </div>\n }\n <mat-slide-toggle\n [(ngModel)]=\"a.emitLocal\"\n (ngModelChange)=\"onActionsChanged()\"\n >{{ tx('Emit local event too') }}</mat-slide-toggle\n >\n }\n </div>\n </div>\n }\n </div>\n </ng-template>\n </mat-tab>\n <mat-tab [label]=\"tx('Layout')\">\n <ng-template matTabContent>\n <div class=\"editor-content grid gap-3\">\n <div class=\"preset-row g row-flow gap-8\">\n <button mat-stroked-button (click)=\"applyLayoutPreset('tiles-modern')\">\n {{ tx('Modern tiles preset') }}\n </button>\n </div>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Variant') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.variant\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n <mat-option value=\"list\">{{ tx('List') }}</mat-option>\n <mat-option value=\"cards\">{{ tx('Cards') }}</mat-option>\n <mat-option value=\"tiles\">{{ tx('Tiles') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Model') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.model\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n @if (working.layout.variant === 'list') {\n <mat-option value=\"standard\">{{ tx('Standard') }}</mat-option>\n <mat-option value=\"media\">{{ tx('Media on the left') }}</mat-option>\n <mat-option value=\"hotel\">{{ tx('Hotel (large media)') }}</mat-option>\n } @else {\n @if (working.layout.variant === 'tiles') {\n <mat-option value=\"standard\">{{ tx('Standard tile') }}</mat-option>\n <mat-option value=\"media\">{{ tx('Tile with media') }}</mat-option>\n <mat-option value=\"hotel\">{{ tx('Hotel tile') }}</mat-option>\n } @else {\n <mat-option value=\"standard\">{{ tx('Standard') }}</mat-option>\n <mat-option value=\"media\">{{ tx('Card with media') }}</mat-option>\n <mat-option value=\"hotel\">{{ tx('Hotel') }}</mat-option>\n }\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Lines') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.lines\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n <mat-option [value]=\"1\">1</mat-option>\n <mat-option [value]=\"2\">2</mat-option>\n <mat-option [value]=\"3\">3</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Items per page') }}</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"1\"\n [(ngModel)]=\"working.layout.pageSize\"\n (ngModelChange)=\"onPageSizeChange($event)\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Density') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.density\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n <mat-option value=\"default\">{{ tx('Default') }}</mat-option>\n <mat-option value=\"comfortable\">{{ tx('Comfortable') }}</mat-option>\n <mat-option value=\"compact\">{{ tx('Compact') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Spacing between items') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.itemSpacing\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n <mat-option value=\"none\">{{ tx('No extra space') }}</mat-option>\n <mat-option value=\"tight\">{{ tx('Tight') }}</mat-option>\n <mat-option value=\"default\">{{ tx('Standard') }}</mat-option>\n <mat-option value=\"relaxed\">{{ tx('Relaxed') }}</mat-option>\n </mat-select>\n </mat-form-field>\n @if (working.layout.variant !== 'tiles') {\n <mat-form-field\n appearance=\"outline\"\n >\n <mat-label>{{ tx('Dividers') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.dividers\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n <mat-option value=\"none\">{{ tx('None') }}</mat-option>\n <mat-option value=\"between\">{{ tx('Between groups') }}</mat-option>\n <mat-option value=\"all\">{{ tx('All') }}</mat-option>\n </mat-select>\n </mat-form-field>\n }\n @if (fields.length > 0) {\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Group by') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.groupBy\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n <mat-option [value]=\"\">{{ tx('None') }}</mat-option>\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n } @else {\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Group by') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.layout.groupBy\"\n (ngModelChange)=\"onLayoutChanged()\"\n [placeholder]=\"tx('e.g.: department')\"\n />\n </mat-form-field>\n }\n <mat-slide-toggle\n [(ngModel)]=\"working.layout.stickySectionHeader\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n {{ tx('Sticky section header') }}\n </mat-slide-toggle>\n <mat-slide-toggle\n [(ngModel)]=\"working.layout.virtualScroll\"\n (ngModelChange)=\"onLayoutChanged()\"\n >\n {{ tx('Virtual scroll') }}\n </mat-slide-toggle>\n <mat-divider class=\"my-8\"></mat-divider>\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ tx('Row layout') }}</mat-panel-title>\n <mat-panel-description>{{\n tx('Stable columns for list rows')\n }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"grid gap-3\">\n <div class=\"g g-1-auto ai-center gap-12\">\n <mat-slide-toggle\n [ngModel]=\"rowLayoutEnabled()\"\n (ngModelChange)=\"onRowLayoutEnabledChange($event)\"\n >{{ tx('Enable row layout') }}</mat-slide-toggle\n >\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"applyCorporateRowLayoutPreset()\"\n >\n {{ tx('Corporate row preset') }}\n </button>\n </div>\n @if (working.layout.rowLayout) {\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Layout type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.rowLayout.type\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n >\n <mat-option value=\"grid\">{{ tx('Grid') }}</mat-option>\n <mat-option value=\"flex\">{{ tx('Flex') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Vertical alignment') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.layout.rowLayout.itemAlignY\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n >\n @for (align of rowLayoutAlignOptions; track align) {\n <mat-option [value]=\"align\">{{ align }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Gap') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.layout.rowLayout.gap\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n placeholder=\"16px\"\n />\n </mat-form-field>\n </div>\n <div class=\"g g-1-auto ai-center gap-8\">\n <div class=\"muted\">\n {{ tx('Columns') }}\n </div>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n (click)=\"addRowLayoutColumn()\"\n >\n {{ tx('Add column') }}\n </button>\n </div>\n @for (\n column of working.layout.rowLayout.columns || [];\n track $index;\n let i = $index\n ) {\n <div class=\"g g-auto-220 gap-12 ai-end mt-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Slot') }}</mat-label>\n <mat-select\n [(ngModel)]=\"column.slot\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n >\n @for (slot of rowLayoutSlotOptions; track slot) {\n <mat-option [value]=\"slot\">{{ slot }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Width') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"column.width\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n placeholder=\"1fr\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Min width') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"column.minWidth\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n placeholder=\"120px\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Max width') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"column.maxWidth\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n placeholder=\"320px\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Align') }}</mat-label>\n <mat-select\n [(ngModel)]=\"column.align\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n >\n @for (align of rowLayoutColumnAlignOptions; track align) {\n <mat-option [value]=\"align\">{{ align }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Justify') }}</mat-label>\n <mat-select\n [(ngModel)]=\"column.justify\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n >\n @for (justify of rowLayoutJustifyOptions; track justify) {\n <mat-option [value]=\"justify\">{{ justify }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"g row-flow gap-4 ai-center\">\n <button\n type=\"button\"\n [praxisIconButton]=\"'arrow_back'\"\n size=\"dense\"\n [disabled]=\"i === 0\"\n [attr.aria-label]=\"tx('Move column left')\"\n (click)=\"moveRowLayoutColumn(i, -1)\"\n ></button>\n <button\n type=\"button\"\n [praxisIconButton]=\"'arrow_forward'\"\n size=\"dense\"\n [disabled]=\"i === (working.layout.rowLayout.columns?.length || 0) - 1\"\n [attr.aria-label]=\"tx('Move column right')\"\n (click)=\"moveRowLayoutColumn(i, 1)\"\n ></button>\n <button\n type=\"button\"\n [praxisIconButton]=\"'delete'\"\n size=\"dense\"\n class=\"editor-icon-action--danger\"\n [attr.aria-label]=\"tx('Remove column')\"\n (click)=\"removeRowLayoutColumn(i)\"\n ></button>\n </div>\n @if (rowLayoutColumnWarning(column); as warning) {\n <div class=\"muted col-span-2\">\n {{ warning }}\n </div>\n }\n </div>\n }\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ tx('Advanced row CSS') }}</mat-panel-title>\n </mat-expansion-panel-header>\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('CSS class') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.layout.rowLayout.class\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Inline style') }}</mat-label>\n <textarea\n matInput\n rows=\"2\"\n [(ngModel)]=\"working.layout.rowLayout.style\"\n (ngModelChange)=\"onRowLayoutChanged()\"\n ></textarea>\n </mat-form-field>\n </div>\n </mat-expansion-panel>\n }\n </div>\n </mat-expansion-panel>\n <mat-divider class=\"my-8\"></mat-divider>\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ tx('Expansion') }}</mat-panel-title>\n <mat-panel-description>{{\n tx('Inline details for list rows')\n }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"grid gap-3\">\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-slide-toggle\n [ngModel]=\"expansionEnabled()\"\n (ngModelChange)=\"onExpansionEnabledChange($event)\"\n >{{ tx('Enable expansion') }}</mat-slide-toggle\n >\n @if (working.interaction?.expandable) {\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Trigger') }}</mat-label>\n <mat-select\n [ngModel]=\"getExpandTriggerModel()\"\n (ngModelChange)=\"onExpansionInteractionChanged('expandTrigger', $event)\"\n >\n @for (trigger of expandTriggerOptions; track trigger) {\n <mat-option [value]=\"trigger\">{{ trigger }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Mode') }}</mat-label>\n <mat-select\n [ngModel]=\"getExpandModeModel()\"\n (ngModelChange)=\"onExpansionInteractionChanged('expandMode', $event)\"\n >\n @for (mode of expandModeOptions; track mode) {\n <mat-option [value]=\"mode\">{{ mode }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Control placement') }}</mat-label>\n <mat-select\n [ngModel]=\"getExpandPlacementModel()\"\n (ngModelChange)=\"onExpansionInteractionChanged('expandPlacement', $event)\"\n >\n @for (placement of expandPlacementOptions; track placement) {\n <mat-option [value]=\"placement\">{{ placement }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n </div>\n @if (working.interaction?.expandable && working.expansion) {\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Shell') }}</mat-label>\n <mat-select\n [ngModel]=\"getExpansionShellModel()\"\n (ngModelChange)=\"onExpansionRenderingChanged('shell', $event)\"\n >\n @for (shell of expansionShellOptions; track shell) {\n <mat-option [value]=\"shell\">{{ shell }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Columns') }}</mat-label>\n <mat-select\n [ngModel]=\"getExpansionColumnsModel()\"\n (ngModelChange)=\"onExpansionRenderingChanged('columns', $event)\"\n >\n @for (columns of expansionColumnOptions; track columns) {\n <mat-option [value]=\"columns\">{{ columns }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Gap') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getExpansionGapModel()\"\n (ngModelChange)=\"onExpansionRenderingChanged('gap', $event)\"\n placeholder=\"12px\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Padding') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getExpansionPaddingModel()\"\n (ngModelChange)=\"onExpansionRenderingChanged('padding', $event)\"\n placeholder=\"16px\"\n />\n </mat-form-field>\n </div>\n <div class=\"grid gap-2\">\n <div class=\"muted\">{{ getExpansionRemoteSummary() }}</div>\n @for (warning of getExpansionRemoteWarnings(); track warning) {\n <div class=\"muted\">{{ warning }}</div>\n }\n </div>\n <div class=\"g g-1-auto ai-center gap-8\">\n <div class=\"muted\">\n {{ tx('Sections') }}\n </div>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n (click)=\"addExpansionSection()\"\n >\n {{ tx('Add section') }}\n </button>\n </div>\n @for (\n section of working.expansion.sections || [];\n track section.id;\n let i = $index\n ) {\n <div class=\"g g-auto-220 gap-12 ai-end mt-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Id') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"section.id\"\n (ngModelChange)=\"onExpansionChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Title') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"section.title\"\n (ngModelChange)=\"onExpansionChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"section.type\"\n (ngModelChange)=\"onExpansionChanged()\"\n >\n @for (type of expansionSectionTypeOptions; track type) {\n <mat-option [value]=\"type\">{{ type }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Items expression') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"section.itemsExpr\"\n (ngModelChange)=\"onExpansionChanged()\"\n placeholder=\"${item.details}\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Empty label') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"section.emptyLabel\"\n (ngModelChange)=\"onExpansionChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Show when (Json Logic)') }}</mat-label>\n <textarea\n matInput\n rows=\"2\"\n [ngModel]=\"getExpansionSectionShowIfText(section)\"\n (ngModelChange)=\"onExpansionSectionShowIfChanged(section, $event)\"\n ></textarea>\n </mat-form-field>\n <div class=\"g row-flow gap-4 ai-center\">\n <button\n type=\"button\"\n [praxisIconButton]=\"'arrow_upward'\"\n size=\"dense\"\n [disabled]=\"i === 0\"\n [attr.aria-label]=\"tx('Move section up')\"\n (click)=\"moveExpansionSection(i, -1)\"\n ></button>\n <button\n type=\"button\"\n [praxisIconButton]=\"'arrow_downward'\"\n size=\"dense\"\n [disabled]=\"i === (working.expansion.sections?.length || 0) - 1\"\n [attr.aria-label]=\"tx('Move section down')\"\n (click)=\"moveExpansionSection(i, 1)\"\n ></button>\n <button\n type=\"button\"\n [praxisIconButton]=\"'delete'\"\n size=\"dense\"\n class=\"editor-icon-action--danger\"\n [attr.aria-label]=\"tx('Remove section')\"\n (click)=\"removeExpansionSection(i)\"\n ></button>\n </div>\n </div>\n }\n }\n </div>\n </mat-expansion-panel>\n <mat-divider class=\"my-8\"></mat-divider>\n <div class=\"subtitle\">{{ tx('List tools') }}</div>\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-slide-toggle\n [(ngModel)]=\"working.ui.showSearch\"\n (ngModelChange)=\"onUiChanged()\"\n >{{ tx('Show search') }}</mat-slide-toggle\n >\n <mat-slide-toggle\n [(ngModel)]=\"working.ui.showSort\"\n (ngModelChange)=\"onUiChanged()\"\n >{{ tx('Show sorting') }}</mat-slide-toggle\n >\n <mat-slide-toggle\n [(ngModel)]=\"working.ui.showRange\"\n (ngModelChange)=\"onUiChanged()\"\n >{{ tx('Show total X-Y range') }}</mat-slide-toggle\n >\n <mat-slide-toggle\n [ngModel]=\"exportEnabled()\"\n (ngModelChange)=\"onExportEnabledChange($event)\"\n >{{ tx('Enable export') }}</mat-slide-toggle\n >\n </div>\n @if (working.export?.enabled) {\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel mt-12\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ tx('Export') }}</mat-panel-title>\n <mat-panel-description>{{\n tx('Formats, scope and file options')\n }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Formats') }}</mat-label>\n <mat-select\n multiple\n [ngModel]=\"getExportFormatsModel()\"\n (ngModelChange)=\"onExportFormatsChange($event)\"\n >\n @for (format of exportFormatOptions; track format) {\n <mat-option [value]=\"format\">{{\n format.toUpperCase()\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Scope') }}</mat-label>\n <mat-select\n [ngModel]=\"getExportScopeModel()\"\n (ngModelChange)=\"onExportScopeChange($event)\"\n >\n @for (scope of exportScopeOptions; track scope) {\n <mat-option [value]=\"scope\">{{ scope }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Max rows') }}</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"1\"\n [ngModel]=\"getExportMaxRowsModel()\"\n (ngModelChange)=\"onExportMaxRowsChange($event)\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Default file name') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getExportDefaultFileNameModel()\"\n (ngModelChange)=\"onExportDefaultFileNameChange($event)\"\n placeholder=\"praxis-list-export\"\n />\n </mat-form-field>\n </div>\n <div class=\"g g-auto-220 gap-12 ai-end mt-12\">\n <mat-form-field appearance=\"outline\" class=\"col-span-2\">\n <mat-label>{{ tx('Included fields') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getExportIncludeFieldsText()\"\n (ngModelChange)=\"onExportIncludeFieldsTextChange($event)\"\n [placeholder]=\"tx('Empty means all fields')\"\n />\n @if (getExportIncludeFieldsWarning(); as warning) {\n <mat-hint>{{ warning }}</mat-hint>\n }\n </mat-form-field>\n <mat-slide-toggle\n [ngModel]=\"getExportIncludeHeadersModel()\"\n (ngModelChange)=\"onExportIncludeHeadersChange($event)\"\n >{{ tx('Include headers') }}</mat-slide-toggle\n >\n <mat-slide-toggle\n [ngModel]=\"getExportApplyFormattingModel()\"\n (ngModelChange)=\"onExportApplyFormattingChange($event)\"\n >{{ tx('Apply formatting') }}</mat-slide-toggle\n >\n </div>\n <div class=\"grid gap-2 mt-12\">\n <div class=\"subtitle\">{{ tx('Export preview') }}</div>\n <div class=\"muted\">{{ getExportPreviewSummary() }}</div>\n @if (getExportPreviewColumns().length) {\n <div class=\"muted\">\n {{ tx('Columns') }}:\n {{ getExportPreviewColumns().join(', ') }}\n </div>\n @for (row of getExportPreviewRows(); track $index) {\n <div class=\"muted\">\n @for (column of getExportPreviewColumns(); track column) {\n <span>{{ column }}={{ row[column] }}</span>\n }\n </div>\n }\n } @else {\n <div class=\"muted\">\n {{ tx('No fields available for preview yet.') }}\n </div>\n }\n </div>\n </mat-expansion-panel>\n }\n @if (working.ui?.showSearch) {\n <div\n class=\"g g-auto-220 gap-12 ai-end mt-12\"\n >\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Field to search') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.ui.searchField\"\n (ngModelChange)=\"onUiChanged()\"\n >\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Search placeholder') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.ui.searchPlaceholder\"\n (ngModelChange)=\"onUiChanged()\"\n [placeholder]=\"tx('e.g.: Search by title')\"\n />\n </mat-form-field>\n </div>\n }\n @if (working.ui?.showSort) {\n <div class=\"mt-12\">\n <div class=\"g g-1-auto ai-center gap-8\">\n <div class=\"muted\">\n {{ tx('Sorting options (label \u2192 field+direction)') }}\n </div>\n <button mat-flat-button color=\"primary\" (click)=\"addUiSortRow()\">\n {{ tx('Add option') }}\n </button>\n </div>\n @for (r of uiSortRows; track $index; let i = $index) {\n <div\n class=\"g g-auto-220 gap-12 ai-end mt-12\"\n >\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Label') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"r.label\"\n (ngModelChange)=\"onUiSortRowsChanged()\"\n [placeholder]=\"tx('e.g.: Most recent')\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Field') }}</mat-label>\n <mat-select\n [(ngModel)]=\"r.field\"\n (ngModelChange)=\"onUiSortRowsChanged()\"\n >\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Direction') }}</mat-label>\n <mat-select\n [(ngModel)]=\"r.dir\"\n (ngModelChange)=\"onUiSortRowsChanged()\"\n >\n <mat-option value=\"desc\">{{ tx('Descending') }}</mat-option>\n <mat-option value=\"asc\">{{ tx('Ascending') }}</mat-option>\n </mat-select>\n </mat-form-field>\n @if (isUiSortRowDuplicate(i)) {\n <div class=\"error\">\n {{ tx('Duplicate option (field+direction)') }}\n </div>\n }\n <div class=\"flex-end\">\n <button mat-button color=\"warn\" (click)=\"removeUiSortRow(i)\">\n {{ tx('Remove') }}\n </button>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </ng-template>\n </mat-tab>\n <mat-tab [label]=\"tx('Content')\">\n <ng-template matTabContent>\n <div class=\"editor-content\">\n <div class=\"editor-main\">\n <mat-accordion multi>\n <!-- Primary -->\n <mat-expansion-panel [expanded]=\"true\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"g row-flow gap-8 ai-center\">\n <mat-icon>{{ getTypeIcon(mappingPrimary.type) }}</mat-icon>\n <span>{{ tx('Primary (Title)') }}</span>\n </div>\n </mat-panel-title>\n <mat-panel-description>{{\n mappingPrimary.field || tx('Not mapped')\n }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g gap-12\">\n <div class=\"g row-flow gap-8\">\n <span class=\"text-caption muted\">{{ tx('Presets') }}</span>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingPrimary.type = 'text';\n mappingPrimary.field = 'name';\n onMappingChanged()\n \"\n >\n {{ tx('Name') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingPrimary.type = 'text';\n mappingPrimary.field = 'title';\n onMappingChanged()\n \"\n >\n {{ tx('Title') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingPrimary.type = 'text';\n mappingPrimary.field = 'name';\n mappingSecondary.type = 'text';\n mappingSecondary.field = 'role';\n onMappingChanged()\n \"\n >\n {{ tx('Name + role') }}\n </button>\n </div>\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Field') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingPrimary.field\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingPrimary.type\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (mt of primaryTypeConfigs; track mt.type) {\n <mat-option\n [value]=\"mt.type\"\n >\n <mat-icon class=\"option-icon\">{{ mt.icon }}</mat-icon>\n {{ tx(mt.label) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n @switch (mappingPrimary.type) {\n @case (\"text\") {\n <praxis-meta-editor-text\n [model]=\"mappingPrimary\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"html\") {\n <praxis-meta-editor-text\n [model]=\"mappingPrimary\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"currency\") {\n <praxis-meta-editor-currency\n [model]=\"mappingPrimary\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-currency>\n }\n @case (\"date\") {\n <praxis-meta-editor-date\n [model]=\"mappingPrimary\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-date>\n }\n }\n\n <!-- Advanced -->\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ tx('Formatting and style') }}</mat-panel-title>\n </mat-expansion-panel-header>\n <div class=\"g gap-12 pt-12\">\n @if (\n mappingPrimary.type === 'text' ||\n mappingPrimary.type === 'html'\n ) {\n <mat-form-field\n appearance=\"outline\"\n >\n <mat-label>{{ tx('CSS class') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingPrimary.class\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n }\n @if (\n mappingPrimary.type === 'text' ||\n mappingPrimary.type === 'html'\n ) {\n <mat-form-field\n appearance=\"outline\"\n >\n <mat-label>{{ tx('Inline style') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingPrimary.style\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n }\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('CSS class') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingPrimary.class\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Inline style') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingPrimary.style\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n </div>\n </div>\n </mat-expansion-panel>\n </div>\n </mat-expansion-panel>\n\n <!-- Secondary -->\n <mat-expansion-panel [expanded]=\"!!mappingSecondary.field\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"g row-flow gap-8 ai-center\">\n <mat-icon>{{ getTypeIcon(mappingSecondary.type) }}</mat-icon>\n <span>{{ tx('Secondary (Summary)') }}</span>\n </div>\n </mat-panel-title>\n <mat-panel-description>{{\n mappingSecondary.field || tx('Not mapped')\n }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g gap-12\">\n <div class=\"g row-flow gap-8\">\n <span class=\"text-caption muted\">{{ tx('Presets') }}</span>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingSecondary.type = 'text';\n mappingSecondary.field = 'subtitle';\n onMappingChanged()\n \"\n >\n {{ tx('Subtitle') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingSecondary.type = 'date';\n mappingSecondary.field = 'hireDate';\n mappingSecondary.dateStyle = 'short';\n onMappingChanged()\n \"\n >\n {{ tx('Short date') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"applySalaryPreset()\"\n >\n {{ tx('Salary') }}\n </button>\n </div>\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Field') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingSecondary.field\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingSecondary.type\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (mt of secondaryTypeConfigs; track mt.type) {\n <mat-option\n [value]=\"mt.type\"\n >\n <mat-icon class=\"option-icon\">{{ mt.icon }}</mat-icon>\n {{ tx(mt.label) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n @switch (mappingSecondary.type) {\n @case (\"text\") {\n <praxis-meta-editor-text\n [model]=\"mappingSecondary\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"html\") {\n <praxis-meta-editor-text\n [model]=\"mappingSecondary\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"currency\") {\n <praxis-meta-editor-currency\n [model]=\"mappingSecondary\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-currency>\n }\n @case (\"date\") {\n <praxis-meta-editor-date\n [model]=\"mappingSecondary\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-date>\n }\n }\n\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ tx('Formatting and style') }}</mat-panel-title>\n </mat-expansion-panel-header>\n <div class=\"g gap-12 pt-12\">\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('CSS class') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingSecondary.class\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Inline style') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingSecondary.style\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n </div>\n </div>\n </mat-expansion-panel>\n </div>\n </mat-expansion-panel>\n\n <mat-expansion-panel\n [expanded]=\"!!mappingMeta.field || mappingMetaFields.length > 0\"\n >\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"g row-flow gap-8 ai-center\">\n <mat-icon>{{\n getTypeIcon(mappingMeta.type || \"text\")\n }}</mat-icon>\n <span>{{ tx('Meta (Detail/Side)') }}</span>\n </div>\n </mat-panel-title>\n <mat-panel-description>\n {{\n mappingMetaFields.length\n ? tx('Composed field ({{count}})', { count: mappingMetaFields.length })\n : mappingMeta.field || tx('Not mapped')\n }}\n </mat-panel-description>\n </mat-expansion-panel-header>\n\n <div class=\"g gap-12\">\n <!-- Composition Mode Toggle -->\n <div class=\"g g-1-1 gap-12 p-12 bg-subtle rounded\">\n <div class=\"text-caption muted\">{{ tx('Composition mode') }}</div>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Fields to compose (multi-select)') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingMetaFields\"\n multiple\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n @if (mappingMetaFields.length) {\n <div\n class=\"g g-1-1 ai-center gap-12\"\n >\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Separator') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingMetaSeparator\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n <mat-slide-toggle\n [(ngModel)]=\"mappingMetaWrapSecondInParens\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n {{ tx('(Second) in parentheses') }}\n </mat-slide-toggle>\n </div>\n }\n </div>\n\n <!-- Single Field Mode (if no composition) -->\n @if (!mappingMetaFields.length) {\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Single field') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingMeta.field\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n <mat-option [value]=\"undefined\">{{ tx('-- None --') }}</mat-option>\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingMeta.type\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (mt of metaTypeConfigs; track mt.type) {\n <mat-option\n [value]=\"mt.type\"\n >\n <mat-icon class=\"option-icon\">{{ mt.icon }}</mat-icon>\n {{ tx(mt.label) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n }\n\n <!-- Type configuration (pluggable editors) -->\n @switch (mappingMeta.type) {\n @case (\"text\") {\n <praxis-meta-editor-text\n [model]=\"mappingMeta\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"html\") {\n <praxis-meta-editor-text\n [model]=\"mappingMeta\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"currency\") {\n <praxis-meta-editor-currency\n [model]=\"mappingMeta\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-currency>\n }\n @case (\"date\") {\n <praxis-meta-editor-date\n [model]=\"mappingMeta\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-date>\n }\n @case (\"chip\") {\n <praxis-meta-editor-chip\n [model]=\"mappingMeta\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-chip>\n }\n @case (\"rating\") {\n <praxis-meta-editor-rating\n [model]=\"mappingMeta\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-rating>\n }\n @case (\"icon\") {\n <praxis-meta-editor-icon\n [model]=\"mappingMeta\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-icon>\n }\n @case (\"image\") {\n <praxis-meta-editor-image\n [model]=\"mappingMeta\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-image>\n }\n }\n\n <!-- Advanced -->\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header\n ><mat-panel-title>{{ tx('Advanced options') }}</mat-panel-title\n ></mat-expansion-panel-header\n >\n <div class=\"g gap-12 pt-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Position') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingMeta.placement\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n <mat-option value=\"side\">{{ tx('Side (right)') }}</mat-option>\n <mat-option value=\"line\">{{ tx('Inline (below)') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('CSS class') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingMeta.class\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Style') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingMeta.style\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n </div>\n </mat-expansion-panel>\n </div>\n </mat-expansion-panel>\n <!-- Trailing -->\n <mat-expansion-panel [expanded]=\"!!mappingTrailing.field\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"g row-flow gap-8 ai-center\">\n <mat-icon>{{\n getTypeIcon(mappingTrailing.type || \"text\")\n }}</mat-icon>\n <span>{{ tx('Trailing (right)') }}</span>\n </div>\n </mat-panel-title>\n <mat-panel-description>{{\n mappingTrailing.field || tx('Not mapped')\n }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g gap-12\">\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Field') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingTrailing.field\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n <mat-option [value]=\"undefined\">{{ tx('-- None --') }}</mat-option>\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingTrailing.type\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (mt of trailingTypeConfigs; track mt.type) {\n <mat-option\n [value]=\"mt.type\"\n >\n <mat-icon class=\"option-icon\">{{ mt.icon }}</mat-icon>\n {{ tx(mt.label) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"g row-flow gap-8\">\n <span class=\"text-caption muted\">{{ tx('Presets') }}</span>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingTrailing.type = 'chip';\n mappingTrailing.chipColor = 'primary';\n mappingTrailing.chipVariant = 'filled';\n mappingTrailing.field = 'status';\n onMappingChanged()\n \"\n >\n {{ tx('Status chip') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingTrailing.type = 'icon';\n mappingTrailing.field = 'status';\n mappingTrailing.iconColor = 'primary';\n onMappingChanged()\n \"\n >\n {{ tx('Status icon') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"applyPricePreset()\"\n >\n {{ tx('Price') }}\n </button>\n </div>\n\n @switch (mappingTrailing.type) {\n @case (\"text\") {\n <praxis-meta-editor-text\n [model]=\"mappingTrailing\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"html\") {\n <praxis-meta-editor-text\n [model]=\"mappingTrailing\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"currency\") {\n <praxis-meta-editor-currency\n [model]=\"mappingTrailing\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-currency>\n }\n @case (\"date\") {\n <praxis-meta-editor-date\n [model]=\"mappingTrailing\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-date>\n }\n @case (\"chip\") {\n <praxis-meta-editor-chip\n [model]=\"mappingTrailing\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-chip>\n }\n @case (\"rating\") {\n <praxis-meta-editor-rating\n [model]=\"mappingTrailing\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-rating>\n }\n @case (\"icon\") {\n <praxis-meta-editor-icon\n [model]=\"mappingTrailing\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-icon>\n }\n @case (\"image\") {\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('URL / Expr') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingTrailing.imageUrl\"\n (ngModelChange)=\"onMappingChanged()\"\n [placeholder]=\"tx('https://... or ${item.imageUrl}')\"\n />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"tx('Use an absolute/relative URL or a ${item.field} expression.')\"\n [attr.aria-label]=\"tx('Use an absolute/relative URL or a ${item.field} expression.')\"\n ></button>\n @if (\n isImageUrlRequiredInvalid(mappingTrailing.imageUrl)\n ) {\n <mat-error\n >{{ tx('URL/expr is required') }}</mat-error\n >\n }\n </mat-form-field>\n </div>\n <praxis-meta-editor-image\n [model]=\"mappingTrailing\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-image>\n @if (!mappingTrailing.imageUrl) {\n <div\n class=\"text-caption muted\"\n >\n {{ tx('Set the URL/expr to render the image.') }}\n </div>\n }\n }\n }\n\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header\n ><mat-panel-title>{{ tx('Style') }}</mat-panel-title\n ></mat-expansion-panel-header\n >\n <div class=\"g gap-12 pt-12\">\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Class') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingTrailing.class\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Style') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingTrailing.style\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n </div>\n </div>\n </mat-expansion-panel>\n </div>\n </mat-expansion-panel>\n\n <!-- Leading -->\n <mat-expansion-panel\n [expanded]=\"\n !!mappingLeading.field ||\n (mappingLeading.type === 'icon' && !!mappingLeading.icon) ||\n (mappingLeading.type === 'image' && !!mappingLeading.imageUrl)\n \"\n >\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"g row-flow gap-8 ai-center\">\n <mat-icon>{{ getTypeIcon(mappingLeading.type) }}</mat-icon>\n <span>{{ tx('Leading (left)') }}</span>\n </div>\n </mat-panel-title>\n <mat-panel-description>\n {{\n mappingLeading.type === \"icon\"\n ? mappingLeading.icon || tx('Static icon')\n : mappingLeading.field ||\n (mappingLeading.imageUrl\n ? tx('Static image')\n : tx('Not mapped'))\n }}\n </mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g gap-12\">\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingLeading.type\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (mt of leadingTypeConfigs; track mt.type) {\n <mat-option\n [value]=\"mt.type\"\n >\n <mat-icon class=\"option-icon\">{{ mt.icon }}</mat-icon>\n {{ tx(mt.label) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <!-- Field (only if not static icon/image, though user might want dynamic) -->\n @if (\n mappingLeading.type !== 'icon' &&\n mappingLeading.type !== 'image'\n ) {\n <mat-form-field\n appearance=\"outline\"\n >\n <mat-label>{{ tx('Field') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingLeading.field\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (f of fields; track f) {\n <mat-option [value]=\"f\">{{\n f\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n </div>\n <div class=\"g row-flow gap-8\">\n <span class=\"text-caption muted\">{{ tx('Presets') }}</span>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingLeading.type = 'icon';\n mappingLeading.icon = 'person';\n mappingLeading.iconColor = 'primary';\n onMappingChanged()\n \"\n >\n {{ tx('Avatar icon') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingLeading.type = 'image';\n mappingLeading.imageUrl = 'https://placehold.co/64x64';\n mappingLeading.imageAlt = 'Avatar';\n mappingLeading.badgeText = '${item.status}';\n onMappingChanged()\n \"\n >\n {{ tx('Avatar image + badge') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingLeading.type = 'chip';\n mappingLeading.field = 'tag';\n mappingLeading.chipColor = 'accent';\n mappingLeading.chipVariant = 'filled';\n onMappingChanged()\n \"\n >\n {{ tx('Tag chip') }}\n </button>\n </div>\n\n <!-- Icon Specific -->\n @if (mappingLeading.type === 'icon') {\n <div\n class=\"g g-1-auto gap-12 ai-center\"\n >\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Icon') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingLeading.icon\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'search'\"\n size=\"dense\"\n [matTooltip]=\"tx('Choose icon')\"\n [attr.aria-label]=\"tx('Choose icon')\"\n (click)=\"pickLeadingIcon()\"\n ></button>\n </mat-form-field>\n <div class=\"text-caption muted\">\n {{ tx('Use the `|iconMap` pipe in the extra pipe for dynamic rendering.') }}\n </div>\n </div>\n }\n @if (mappingLeading.type === 'icon') {\n <div>\n <praxis-meta-editor-icon\n [model]=\"mappingLeading\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground\"\n [isCustomColor]=\"isCustomColor\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-icon>\n </div>\n }\n\n <!-- Image Specific -->\n @if (mappingLeading.type === 'image') {\n <div\n class=\"g g-1-1 gap-12\"\n >\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Image URL') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingLeading.imageUrl\"\n (ngModelChange)=\"onMappingChanged()\"\n [placeholder]=\"tx('https://... or ${item.imageUrl}')\"\n />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"tx('Use an absolute/relative URL or a ${item.field} expression.')\"\n [attr.aria-label]=\"tx('Use an absolute/relative URL or a ${item.field} expression.')\"\n ></button>\n @if (isImageUrlRequiredInvalid(mappingLeading.imageUrl)) {\n <mat-error\n >{{ tx('URL/expr is required') }}</mat-error\n >\n }\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Alt text') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingLeading.imageAlt\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Badge text') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingLeading.badgeText\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n </div>\n }\n\n @switch (mappingLeading.type) {\n @case (\"text\") {\n <praxis-meta-editor-text\n [model]=\"mappingLeading\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"html\") {\n <praxis-meta-editor-text\n [model]=\"mappingLeading\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"chip\") {\n <praxis-meta-editor-chip\n [model]=\"mappingLeading\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-chip>\n }\n @case (\"rating\") {\n <praxis-meta-editor-rating\n [model]=\"mappingLeading\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-rating>\n }\n }\n\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header\n ><mat-panel-title>{{ tx('Style') }}</mat-panel-title\n ></mat-expansion-panel-header\n >\n <div class=\"g gap-12 pt-12\">\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Class') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingLeading.class\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Style') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingLeading.style\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n </div>\n </mat-expansion-panel>\n </div>\n </mat-expansion-panel>\n\n <!-- Features -->\n <mat-expansion-panel\n [expanded]=\"featuresVisible && features.length > 0\"\n >\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"g row-flow gap-8 ai-center\">\n <mat-icon>view_list</mat-icon>\n <span>{{ tx('Features') }}</span>\n </div>\n </mat-panel-title>\n <mat-panel-description\n >{{ features.length }} item(s)</mat-panel-description\n >\n </mat-expansion-panel-header>\n\n <div class=\"g gap-12\">\n <div class=\"g row-flow gap-12 ai-center\">\n <mat-slide-toggle\n [(ngModel)]=\"featuresVisible\"\n (ngModelChange)=\"onFeaturesChanged()\"\n >{{ tx('Enable features') }}</mat-slide-toggle\n >\n <mat-slide-toggle\n [(ngModel)]=\"featuresSyncWithMeta\"\n (ngModelChange)=\"onMappingChanged()\"\n >{{ tx('Sync with Meta') }}</mat-slide-toggle\n >\n <span class=\"flex-1\"></span>\n <mat-button-toggle-group\n [(ngModel)]=\"featuresMode\"\n (change)=\"onFeaturesChanged()\"\n appearance=\"legacy\"\n >\n <mat-button-toggle value=\"icons+labels\"\n ><mat-icon>view_list</mat-icon></mat-button-toggle\n >\n <mat-button-toggle value=\"icons-only\"\n ><mat-icon>more_horiz</mat-icon></mat-button-toggle\n >\n </mat-button-toggle-group>\n </div>\n\n @for (f of features; track $index; let i = $index) {\n <div\n class=\"g g-auto-1 gap-8 ai-center p-8 border rounded mb-2\"\n >\n <button\n type=\"button\"\n [praxisIconButton]=\"f.icon || 'search'\"\n size=\"dense\"\n [matTooltip]=\"tx('Choose feature icon')\"\n [attr.aria-label]=\"tx('Choose feature icon')\"\n (click)=\"pickFeatureIcon(i)\"\n ></button>\n <mat-form-field\n appearance=\"outline\"\n class=\"dense-form-field no-sub\"\n >\n <input\n matInput\n [(ngModel)]=\"f.expr\"\n (ngModelChange)=\"onFeaturesChanged()\"\n [placeholder]=\"tx('Expression/Text')\"\n />\n </mat-form-field>\n <button\n type=\"button\"\n [praxisIconButton]=\"'delete'\"\n size=\"dense\"\n class=\"editor-icon-action--danger\"\n [matTooltip]=\"tx('Remove feature')\"\n [attr.aria-label]=\"tx('Remove feature')\"\n (click)=\"removeFeature(i)\"\n ></button>\n </div>\n }\n <button mat-button color=\"primary\" (click)=\"addFeature()\">\n <mat-icon>add</mat-icon> {{ tx('Add feature') }}\n </button>\n </div>\n </mat-expansion-panel>\n <!-- Section Header -->\n <mat-expansion-panel [expanded]=\"!!mappingSectionHeader.expr\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"g row-flow gap-8 ai-center\">\n <mat-icon>{{\n getTypeIcon(mappingSectionHeader.type)\n }}</mat-icon>\n <span>{{ tx('Section header') }}</span>\n </div>\n </mat-panel-title>\n <mat-panel-description>{{\n mappingSectionHeader.expr || tx('Not configured')\n }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g gap-12\">\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingSectionHeader.type\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (mt of sectionHeaderTypeConfigs; track mt.type) {\n <mat-option\n [value]=\"mt.type\"\n >\n <mat-icon class=\"option-icon\">{{ mt.icon }}</mat-icon>\n {{ tx(mt.label) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Expression (item.key)') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingSectionHeader.expr\"\n (ngModelChange)=\"onMappingChanged()\"\n placeholder=\"item.key\"\n />\n </mat-form-field>\n </div>\n <div class=\"g row-flow gap-8\">\n <span class=\"text-caption muted\">{{ tx('Presets') }}</span>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingSectionHeader.type = 'text';\n mappingSectionHeader.expr = '${item.key}';\n onMappingChanged()\n \"\n >\n {{ tx('Default text') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingSectionHeader.type = 'chip';\n mappingSectionHeader.chipColor = 'primary';\n mappingSectionHeader.chipVariant = 'filled';\n mappingSectionHeader.expr = '${item.key}';\n onMappingChanged()\n \"\n >\n {{ tx('Default chip') }}\n </button>\n </div>\n\n @switch (mappingSectionHeader.type) {\n @case (\"text\") {\n <praxis-meta-editor-text\n [model]=\"mappingSectionHeader\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"html\") {\n <praxis-meta-editor-text\n [model]=\"mappingSectionHeader\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"chip\") {\n <praxis-meta-editor-chip\n [model]=\"mappingSectionHeader\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-chip>\n }\n @case (\"rating\") {\n <praxis-meta-editor-rating\n [model]=\"mappingSectionHeader\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-rating>\n }\n @case (\"icon\") {\n <praxis-meta-editor-icon\n [model]=\"mappingSectionHeader\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-icon>\n }\n @case (\"image\") {\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Image URL') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingSectionHeader.imageUrl\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n @if (\n isImageUrlRequiredInvalid(\n mappingSectionHeader.imageUrl\n )\n ) {\n <mat-error\n >{{ tx('URL/expr is required') }}</mat-error\n >\n }\n </mat-form-field>\n </div>\n @if (!mappingSectionHeader.imageUrl) {\n <div\n class=\"text-caption muted\"\n >\n {{ tx('Set the URL/expr to render the image.') }}\n </div>\n }\n <praxis-meta-editor-image\n [model]=\"mappingSectionHeader\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-image>\n }\n }\n\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header\n ><mat-panel-title>{{ tx('Style') }}</mat-panel-title\n ></mat-expansion-panel-header\n >\n <div class=\"g gap-12 pt-12\">\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Class') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingSectionHeader.class\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Style') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingSectionHeader.style\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n </div>\n </mat-expansion-panel>\n </div>\n </mat-expansion-panel>\n\n <!-- Empty State -->\n <mat-expansion-panel [expanded]=\"!!mappingEmptyState.expr\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"g row-flow gap-8 ai-center\">\n <mat-icon>inbox</mat-icon>\n <span>{{ tx('Empty state') }}</span>\n </div>\n </mat-panel-title>\n <mat-panel-description>{{\n mappingEmptyState.expr || tx('Default')\n }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g gap-12\">\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Type') }}</mat-label>\n <mat-select\n [(ngModel)]=\"mappingEmptyState.type\"\n (ngModelChange)=\"onMappingChanged()\"\n >\n @for (mt of emptyStateTypeConfigs; track mt.type) {\n <mat-option\n [value]=\"mt.type\"\n >\n <mat-icon class=\"option-icon\">{{ mt.icon }}</mat-icon>\n {{ tx(mt.label) }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Message / Expr') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"mappingEmptyState.expr\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n </mat-form-field>\n </div>\n <div class=\"g row-flow gap-8\">\n <span class=\"text-caption muted\">{{ tx('Presets') }}</span>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingEmptyState.type = 'text';\n mappingEmptyState.expr = tx('No items available');\n onMappingChanged()\n \"\n >\n {{ tx('Default message') }}\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"\n mappingEmptyState.type = 'image';\n mappingEmptyState.imageUrl = '/list-empty-state.svg';\n mappingEmptyState.imageAlt = tx('No results');\n onMappingChanged()\n \"\n >\n {{ tx('Default image') }}\n </button>\n </div>\n\n @switch (mappingEmptyState.type) {\n @case (\"text\") {\n <praxis-meta-editor-text\n [model]=\"mappingEmptyState\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"html\") {\n <praxis-meta-editor-text\n [model]=\"mappingEmptyState\"\n [setPipe]=\"setPipe.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-text>\n }\n @case (\"chip\") {\n <praxis-meta-editor-chip\n [model]=\"mappingEmptyState\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground.bind(this)\"\n [isCustomColor]=\"isCustomColor.bind(this)\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-chip>\n }\n @case (\"rating\") {\n <praxis-meta-editor-rating\n [model]=\"mappingEmptyState\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground\"\n [isCustomColor]=\"isCustomColor\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-rating>\n }\n @case (\"icon\") {\n <praxis-meta-editor-icon\n [model]=\"mappingEmptyState\"\n [paletteOptions]=\"paletteOptions\"\n [colorDotBackground]=\"colorDotBackground\"\n [isCustomColor]=\"isCustomColor\"\n [enableCustomColor]=\"enableCustomColor.bind(this)\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-icon>\n }\n @case (\"image\") {\n <div class=\"g g-1-1 gap-12\">\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Image URL') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingEmptyState.imageUrl\"\n (ngModelChange)=\"onMappingChanged()\"\n />\n @if (\n isImageUrlRequiredInvalid(mappingEmptyState.imageUrl)\n ) {\n <mat-error\n >{{ tx('URL/expr is required') }}</mat-error\n >\n }\n </mat-form-field>\n </div>\n @if (!mappingEmptyState.imageUrl) {\n <div\n class=\"text-caption muted\"\n >\n {{ tx('Set the URL/expr to render the image.') }}\n </div>\n }\n <praxis-meta-editor-image\n [model]=\"mappingEmptyState\"\n (change)=\"onMappingChanged()\"\n ></praxis-meta-editor-image>\n }\n }\n\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\">\n <mat-expansion-panel-header\n ><mat-panel-title>{{ tx('Style') }}</mat-panel-title\n ></mat-expansion-panel-header\n >\n <div class=\"g gap-12 pt-12\">\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Class') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingEmptyState.class\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n <mat-form-field appearance=\"outline\"\n ><mat-label>{{ tx('Style') }}</mat-label\n ><input\n matInput\n [(ngModel)]=\"mappingEmptyState.style\"\n (ngModelChange)=\"onMappingChanged()\"\n /></mat-form-field>\n </div>\n </mat-expansion-panel>\n </div>\n </mat-expansion-panel>\n </mat-accordion>\n\n <button mat-flat-button color=\"primary\" (click)=\"applyTemplate()\">\n {{ tx('Apply mapping') }}\n </button>\n <button\n mat-button\n (click)=\"inferFromFields()\"\n [disabled]=\"!fields.length\"\n >\n {{ tx('Infer from schema') }}\n </button>\n <div class=\"g g-auto-220 gap-12 ai-end mt-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Skeleton count') }}</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n [(ngModel)]=\"skeletonCountInput\"\n (ngModelChange)=\"onSkeletonChanged($event)\"\n />\n </mat-form-field>\n </div>\n\n <div class=\"g gap-12 mt-12\">\n <div class=\"g row-flow gap-8 ai-center\">\n <span class=\"section-title mat-subtitle-1\">{{ tx('Theme preview') }}</span>\n <mat-button-toggle-group\n [(ngModel)]=\"skinPreviewTheme\"\n (change)=\"onSkinChanged()\"\n appearance=\"legacy\"\n >\n <mat-button-toggle [value]=\"'light'\">{{ tx('Light') }}</mat-button-toggle>\n <mat-button-toggle [value]=\"'dark'\">{{ tx('Dark') }}</mat-button-toggle>\n <mat-button-toggle [value]=\"'grid'\">{{ tx('Grid') }}</mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n <div class=\"skin-preview-wrap\">\n <praxis-list-skin-preview\n [config]=\"working\"\n [items]=\"previewData\"\n [theme]=\"skinPreviewTheme\"\n ></praxis-list-skin-preview>\n </div>\n </div>\n </div>\n </div>\n </ng-template>\n </mat-tab>\n <mat-tab [label]=\"tx('Rules')\">\n <ng-template matTabContent>\n <div class=\"editor-content grid gap-3\">\n <div class=\"g g-1-auto ai-center gap-8\">\n <div>\n <div class=\"subtitle\">{{ tx('Item style rules') }}</div>\n <div class=\"muted\">\n {{ tx('Apply class, style, border or background when Json Logic matches.') }}\n </div>\n </div>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n (click)=\"addItemStyleRule()\"\n >\n {{ tx('Add item rule') }}\n </button>\n </div>\n @for (\n rule of working.rules?.itemStyles || [];\n track rule.id;\n let i = $index\n ) {\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\" [expanded]=\"i === 0\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ rule.id || tx('Item rule') }}</mat-panel-title>\n <mat-panel-description>{{ rule.class || rule.background || rule.border }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Id') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"rule.id\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"col-span-2\">\n <mat-label>{{ tx('Condition (Json Logic)') }}</mat-label>\n <textarea\n matInput\n rows=\"3\"\n [ngModel]=\"getRuleConditionText(rule)\"\n (ngModelChange)=\"onRuleConditionChanged(rule, $event)\"\n ></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('CSS class') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"rule.class\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Inline style') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"rule.style\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Border') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"rule.border\"\n (ngModelChange)=\"onRulesChanged()\"\n placeholder=\"1px solid var(--md-sys-color-outline)\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Background') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"rule.background\"\n (ngModelChange)=\"onRulesChanged()\"\n placeholder=\"var(--md-sys-color-surface-container)\"\n />\n </mat-form-field>\n <div class=\"flex-end\">\n <button\n mat-button\n color=\"warn\"\n type=\"button\"\n (click)=\"removeItemStyleRule(i)\"\n >\n {{ tx('Remove') }}\n </button>\n </div>\n <div class=\"col-span-2\">\n <div class=\"g g-1-auto ai-center gap-8\">\n <div>\n <div class=\"subtitle\">{{ tx('Canonical effect') }}</div>\n <div class=\"muted\">\n {{ tx('Materialize the first effects[] entry used by the runtime.') }}\n </div>\n </div>\n @if (getItemStyleEffect(rule)) {\n <button\n mat-button\n type=\"button\"\n (click)=\"clearItemStyleEffects(rule)\"\n >\n {{ tx('Use direct fields') }}\n </button>\n } @else {\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"ensureItemStyleEffect(rule)\"\n >\n {{ tx('Use effects[]') }}\n </button>\n }\n </div>\n </div>\n @if (getItemStyleEffect(rule); as effect) {\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Effect class') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"effect.class\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Effect style') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"effect.style\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Effect border') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"effect.border\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Effect background') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"effect.background\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n }\n </div>\n </mat-expansion-panel>\n }\n\n <mat-divider class=\"my-8\"></mat-divider>\n\n <div class=\"g g-1-auto ai-center gap-8\">\n <div>\n <div class=\"subtitle\">{{ tx('Slot override rules') }}</div>\n <div class=\"muted\">\n {{ tx('Replace, decorate or hide a slot when Json Logic matches.') }}\n </div>\n </div>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n (click)=\"addSlotOverrideRule()\"\n >\n {{ tx('Add slot rule') }}\n </button>\n </div>\n @for (\n rule of working.rules?.slotOverrides || [];\n track rule.id;\n let i = $index\n ) {\n <mat-expansion-panel class=\"mat-elevation-z0 advanced-panel\" [expanded]=\"i === 0\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ rule.id || tx('Slot rule') }}</mat-panel-title>\n <mat-panel-description>{{ rule.slot }}</mat-panel-description>\n </mat-expansion-panel-header>\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Id') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"rule.id\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Slot') }}</mat-label>\n <mat-select\n [(ngModel)]=\"rule.slot\"\n (ngModelChange)=\"onRulesChanged()\"\n >\n @for (slot of ruleSlotOptions; track slot) {\n <mat-option [value]=\"slot\">{{ slot }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"col-span-2\">\n <mat-label>{{ tx('Condition (Json Logic)') }}</mat-label>\n <textarea\n matInput\n rows=\"3\"\n [ngModel]=\"getRuleConditionText(rule)\"\n (ngModelChange)=\"onRuleConditionChanged(rule, $event)\"\n ></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Template type') }}</mat-label>\n <mat-select\n [ngModel]=\"getSlotOverrideTemplateType(rule)\"\n (ngModelChange)=\"onSlotOverrideTemplateTypeChanged(rule, $event)\"\n >\n @for (type of ruleTemplateTypeOptions; track type) {\n <mat-option [value]=\"type\">{{ tx(type) }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Template expression') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getSlotOverrideTemplateExpr(rule)\"\n (ngModelChange)=\"onSlotOverrideTemplateExprChanged(rule, $event)\"\n placeholder=\"${item.status}\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('CSS class') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"rule.class\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Inline style') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"rule.style\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-slide-toggle\n [(ngModel)]=\"rule.hide\"\n (ngModelChange)=\"onRulesChanged()\"\n >{{ tx('Hide slot') }}</mat-slide-toggle>\n <div class=\"flex-end\">\n <button\n mat-button\n color=\"warn\"\n type=\"button\"\n (click)=\"removeSlotOverrideRule(i)\"\n >\n {{ tx('Remove') }}\n </button>\n </div>\n <div class=\"col-span-2\">\n <div class=\"g g-1-auto ai-center gap-8\">\n <div>\n <div class=\"subtitle\">{{ tx('Canonical effect') }}</div>\n <div class=\"muted\">\n {{ tx('Materialize the first effects[] entry used by the runtime.') }}\n </div>\n </div>\n @if (getSlotOverrideEffect(rule)) {\n <button\n mat-button\n type=\"button\"\n (click)=\"clearSlotOverrideEffects(rule)\"\n >\n {{ tx('Use direct fields') }}\n </button>\n } @else {\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"ensureSlotOverrideEffect(rule)\"\n >\n {{ tx('Use effects[]') }}\n </button>\n }\n </div>\n </div>\n @if (getSlotOverrideEffect(rule); as effect) {\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Effect template type') }}</mat-label>\n <mat-select\n [ngModel]=\"getSlotOverrideTemplateType(effect)\"\n (ngModelChange)=\"onSlotOverrideTemplateTypeChanged(effect, $event)\"\n >\n @for (type of ruleTemplateTypeOptions; track type) {\n <mat-option [value]=\"type\">{{ tx(type) }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Effect template expression') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getSlotOverrideTemplateExpr(effect)\"\n (ngModelChange)=\"onSlotOverrideTemplateExprChanged(effect, $event)\"\n placeholder=\"${item.status}\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Effect class') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"effect.class\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Effect style') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"effect.style\"\n (ngModelChange)=\"onRulesChanged()\"\n />\n </mat-form-field>\n <mat-slide-toggle\n [(ngModel)]=\"effect.hide\"\n (ngModelChange)=\"onRulesChanged()\"\n >{{ tx('Effect hides slot') }}</mat-slide-toggle>\n }\n </div>\n </mat-expansion-panel>\n }\n </div>\n </ng-template>\n </mat-tab>\n <mat-tab [label]=\"tx('i18n/A11y')\">\n <ng-template matTabContent>\n @if (working?.a11y && working?.events) {\n <div\n class=\"editor-content grid gap-3\"\n >\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Default locale') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.i18n.locale\"\n (ngModelChange)=\"markDirty()\"\n [placeholder]=\"tx('e.g.: en-US')\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Default currency') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.i18n.currency\"\n (ngModelChange)=\"markDirty()\"\n [placeholder]=\"currencyPlaceholder()\"\n />\n </mat-form-field>\n <mat-divider class=\"my-8\"></mat-divider>\n <div class=\"subtitle\">{{ tx('Accessibility') }}</div>\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('aria-label') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working!.a11y!.ariaLabel\"\n (ngModelChange)=\"markDirty()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('aria-labelledby') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working!.a11y!.ariaLabelledBy\"\n (ngModelChange)=\"markDirty()\"\n />\n </mat-form-field>\n </div>\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-slide-toggle\n [(ngModel)]=\"working!.a11y!.highContrast\"\n (ngModelChange)=\"markDirty()\"\n >{{ tx('High contrast') }}</mat-slide-toggle\n >\n <mat-slide-toggle\n [(ngModel)]=\"working!.a11y!.reduceMotion\"\n (ngModelChange)=\"markDirty()\"\n >{{ tx('Reduce motion') }}</mat-slide-toggle\n >\n </div>\n <mat-divider class=\"my-8\"></mat-divider>\n <div class=\"subtitle\">{{ tx('Events') }}</div>\n <div class=\"g g-auto-220 gap-12 ai-end\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('itemClick') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working!.events!.itemClick\"\n (ngModelChange)=\"markDirty()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>actionClick</mat-label>\n <input\n matInput\n [(ngModel)]=\"working!.events!.actionClick\"\n (ngModelChange)=\"markDirty()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>selectionChange</mat-label>\n <input\n matInput\n [(ngModel)]=\"working!.events!.selectionChange\"\n (ngModelChange)=\"markDirty()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>exportAction</mat-label>\n <input\n matInput\n [(ngModel)]=\"working!.events!.exportAction\"\n (ngModelChange)=\"markDirty()\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>loaded</mat-label>\n <input\n matInput\n [(ngModel)]=\"working!.events!.loaded\"\n (ngModelChange)=\"markDirty()\"\n />\n </mat-form-field>\n </div>\n </div>\n }\n </ng-template>\n </mat-tab>\n <mat-tab [label]=\"tx('Selection')\">\n <ng-template matTabContent>\n <div class=\"editor-content grid gap-3\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Mode') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.selection.mode\"\n (ngModelChange)=\"onSelectionChanged()\"\n >\n <mat-option value=\"none\">{{ tx('No selection') }}</mat-option>\n <mat-option value=\"single\">{{ tx('Single') }}</mat-option>\n <mat-option value=\"multiple\">{{ tx('Multiple') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Form name') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.selection.formControlName\"\n (ngModelChange)=\"onSelectionChanged()\"\n />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n matTooltip=\"formControlName\"\n aria-label=\"formControlName\"\n ></button>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Form path') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.selection.formControlPath\"\n (ngModelChange)=\"onSelectionChanged()\"\n />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n matTooltip=\"formControlPath\"\n aria-label=\"formControlPath\"\n ></button>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Compare by (field)') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.selection.compareBy\"\n (ngModelChange)=\"onSelectionChanged()\"\n />\n <button\n matSuffix\n type=\"button\"\n [praxisIconButton]=\"'help_outline'\"\n size=\"dense\"\n class=\"help-icon-button\"\n [matTooltip]=\"tx('Unique item key.')\"\n [attr.aria-label]=\"tx('Unique item key.')\"\n ></button>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Return') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.selection.return\"\n (ngModelChange)=\"onSelectionChanged()\"\n >\n <mat-option value=\"value\">{{ tx('Value') }}</mat-option>\n <mat-option value=\"item\">{{ tx('Item') }}</mat-option>\n <mat-option value=\"id\">{{ tx('ID') }}</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n </ng-template>\n </mat-tab>\n <mat-tab [label]=\"tx('Appearance')\">\n <ng-template matTabContent>\n <div class=\"editor-content grid gap-3\">\n <div class=\"preset-row g row-flow gap-8\">\n <button mat-button (click)=\"applySkinPreset('pill-soft')\">\n {{ tx('Pill Soft') }}\n </button>\n <button mat-button (click)=\"applySkinPreset('gradient-tile')\">\n {{ tx('Gradient Tile') }}\n </button>\n <button mat-button (click)=\"applySkinPreset('glass')\">\n {{ tx('Glass') }}\n </button>\n <button mat-button (click)=\"applySkinPreset('elevated')\">\n {{ tx('Elevated') }}\n </button>\n <button mat-button (click)=\"applySkinPreset('outline')\">\n {{ tx('Outline') }}\n </button>\n <button mat-button (click)=\"applySkinPreset('flat')\">\n {{ tx('Flat') }}\n </button>\n <button mat-button (click)=\"applySkinPreset('neumorphism')\">\n {{ tx('Neumorphism') }}\n </button>\n </div>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Style') }}</mat-label>\n <mat-select\n [(ngModel)]=\"working.skin.type\"\n (ngModelChange)=\"onSkinTypeChanged($event)\"\n >\n <mat-option value=\"pill-soft\">{{ tx('Pill Soft') }}</mat-option>\n <mat-option value=\"gradient-tile\">{{ tx('Gradient Tile') }}</mat-option>\n <mat-option value=\"glass\">{{ tx('Glass') }}</mat-option>\n <mat-option value=\"elevated\">{{ tx('Elevated') }}</mat-option>\n <mat-option value=\"outline\">{{ tx('Outline') }}</mat-option>\n <mat-option value=\"flat\">{{ tx('Flat') }}</mat-option>\n <mat-option value=\"neumorphism\">{{ tx('Neumorphism') }}</mat-option>\n <mat-option value=\"custom\">{{ tx('Custom') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Radius') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.skin.radius\"\n (ngModelChange)=\"onSkinChanged()\"\n [placeholder]=\"tx('e.g.: 1.25rem')\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Shadow') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.skin.shadow\"\n (ngModelChange)=\"onSkinChanged()\"\n [placeholder]=\"tx('e.g.: var(--md-sys-elevation-level2)')\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Border') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.skin.border\"\n (ngModelChange)=\"onSkinChanged()\"\n />\n </mat-form-field>\n @if (working.skin.type === 'glass') {\n <mat-form-field\n appearance=\"outline\"\n >\n <mat-label>{{ tx('Blur') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.skin.backdropBlur\"\n (ngModelChange)=\"onSkinChanged()\"\n [placeholder]=\"tx('e.g.: 8px')\"\n />\n </mat-form-field>\n }\n @if (working.skin.type === 'gradient-tile') {\n <div class=\"g gap-12\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Gradient from') }}</mat-label>\n <input\n matInput\n [ngModel]=\"working.skin.gradient.from || ''\"\n (ngModelChange)=\"onSkinGradientChanged('from', $event)\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Gradient to') }}</mat-label>\n <input\n matInput\n [ngModel]=\"working.skin.gradient.to || ''\"\n (ngModelChange)=\"onSkinGradientChanged('to', $event)\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Angle') }}</mat-label>\n <input\n matInput\n type=\"number\"\n [ngModel]=\"working.skin.gradient.angle ?? 135\"\n (ngModelChange)=\"onSkinGradientChanged('angle', $event)\"\n />\n </mat-form-field>\n </div>\n }\n\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('Extra CSS class (skin.class)') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"working.skin.class\"\n (ngModelChange)=\"onSkinChanged()\"\n [placeholder]=\"tx('e.g.: my-list-skin')\"\n />\n </mat-form-field>\n\n @if (working.skin.type === 'custom') {\n <div\n class=\"g g-auto-220 gap-12 ai-end\"\n >\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>{{ tx('Inline style (skin.inlineStyle)') }}</mat-label>\n <textarea\n matInput\n rows=\"4\"\n [(ngModel)]=\"working.skin.inlineStyle\"\n (ngModelChange)=\"onSkinChanged()\"\n [attr.placeholder]=\"':host{--p-list-radius: 1rem}'\"\n ></textarea>\n </mat-form-field>\n <div class=\"text-caption\">\n {{ tx('CSS class example (add this to your global styles):') }}\n <pre class=\"code-block\">\n .my-list-skin .item-card {\n border-radius: 14px;\n border: 1px solid var(--md-sys-color-outline-variant);\n box-shadow: var(--md-sys-elevation-level2);\n }\n .my-list-skin .mat-mdc-list-item .list-item-content {\n backdrop-filter: blur(6px);\n }</pre\n >\n </div>\n </div>\n }\n </div>\n </ng-template>\n </mat-tab>\n </mat-tab-group>\n", styles: [".confirm-type{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;font-size:11px;line-height:16px;background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.confirm-type.danger{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.confirm-type.warning{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container)}.confirm-type.info{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}:host{display:block;color:var(--md-sys-color-on-surface)}.list-editor-tabs{--editor-surface: var(--md-sys-color-surface-container-lowest);--editor-border: 1px solid var(--md-sys-color-outline-variant);--editor-radius: var(--md-sys-shape-corner-large, 16px);--editor-muted: var(--md-sys-color-on-surface-variant);--editor-accent: var(--md-sys-color-primary)}.editor-content{padding:16px;background:var(--editor-surface);border:var(--editor-border);border-radius:var(--editor-radius);display:grid;gap:12px}.editor-content .mat-mdc-form-field{width:100%;max-width:none;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var( --md-sys-color-outline-variant );--mdc-outlined-text-field-hover-outline-color: var( --md-sys-color-outline );--mdc-outlined-text-field-focus-outline-color: var( --md-sys-color-primary );--mdc-outlined-text-field-error-outline-color: var( --md-sys-color-error );--mdc-outlined-text-field-error-focus-outline-color: var( --md-sys-color-error );--mdc-outlined-text-field-error-hover-outline-color: var( --md-sys-color-error );--mdc-outlined-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-outlined-text-field-input-text-color: var( --md-sys-color-on-surface );--mdc-outlined-text-field-supporting-text-color: var( --md-sys-color-on-surface-variant )}.editor-content .mat-mdc-form-field.w-full{max-width:none}.help-icon-button{vertical-align:middle}.editor-icon-action--danger{color:var(--md-sys-color-error)}.action-icon-preview{display:inline-grid;place-items:center;width:36px;height:36px;color:var(--md-sys-color-on-surface-variant)}.action-icon-preview mat-icon{width:18px;height:18px;font-size:18px;line-height:18px}.editor-split{grid-template-columns:minmax(0,1fr);align-items:start}.editor-main,.editor-aside{display:grid;gap:12px}.skin-preview-wrap{border-radius:calc(var(--editor-radius) - 4px);border:var(--editor-border);background:var(--md-sys-color-surface-container);padding:12px}.g{display:grid}.g-auto-220{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.g-auto-200{grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}.g-1-auto{grid-template-columns:1fr auto}.row-flow{grid-auto-flow:column}.gap-6{gap:6px}.gap-8{gap:8px}.gap-12{gap:12px}.ai-center{align-items:center}.ai-end{align-items:end}.mt-12{margin-top:12px}.mb-8{margin-bottom:8px}.mb-6{margin-bottom:6px}.my-8{margin:8px 0}.subtitle{margin:8px 0 4px;color:var(--editor-muted);font-weight:500}.section-title{color:var(--editor-muted);font-weight:600}.chips-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.error{color:var(--md-sys-color-error);font-size:.85rem}.muted{color:var(--editor-muted)}.text-caption{color:var(--editor-muted);font-size:.8rem}:host ::ng-deep .mat-mdc-select-panel .option-icon{font-size:18px;margin-right:6px;vertical-align:middle}:host ::ng-deep .mat-mdc-select-panel .color-dot{width:10px;height:10px;border-radius:999px;display:inline-block;margin-right:6px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-outline)}:host ::ng-deep .mat-mdc-select-panel .color-primary{background:var(--md-sys-color-primary)}:host ::ng-deep .mat-mdc-select-panel .color-accent{background:var(--md-sys-color-tertiary)}:host ::ng-deep .mat-mdc-select-panel .color-warn{background:var(--md-sys-color-error)}:host ::ng-deep .mat-mdc-select-panel .color-default{background:var(--md-sys-color-outline)}@media(max-width:1024px){.editor-split{grid-template-columns:minmax(0,1fr)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i3$2.MatTabContent, selector: "[matTabContent]" }, { kind: "component", type: i3$2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i3$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: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i2$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.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: i4$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i7.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: MatSlideToggleModule }, { kind: "component", type: i8.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: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i10.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i10.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i10.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i10.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i10.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i11.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i11.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i12.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i2.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "ngmodule", type: MatMenuModule }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "component", type: PraxisListSkinPreviewComponent, selector: "praxis-list-skin-preview", inputs: ["config", "items", "theme"] }, { kind: "component", type: PraxisMetaEditorTextComponent, selector: "praxis-meta-editor-text", inputs: ["model", "setPipe"], outputs: ["change"] }, { kind: "component", type: PraxisMetaEditorChipComponent, selector: "praxis-meta-editor-chip", inputs: ["model", "paletteOptions", "colorDotBackground", "isCustomColor", "enableCustomColor"], outputs: ["change"] }, { kind: "component", type: PraxisMetaEditorRatingComponent, selector: "praxis-meta-editor-rating", inputs: ["model", "paletteOptions", "colorDotBackground", "isCustomColor", "enableCustomColor"], outputs: ["change"] }, { kind: "component", type: PraxisMetaEditorCurrencyComponent, selector: "praxis-meta-editor-currency", inputs: ["model"], outputs: ["change"] }, { kind: "component", type: PraxisMetaEditorDateComponent, selector: "praxis-meta-editor-date", inputs: ["model"], outputs: ["change"] }, { kind: "component", type: PraxisMetaEditorIconComponent, selector: "praxis-meta-editor-icon", inputs: ["model", "paletteOptions", "colorDotBackground", "isCustomColor", "enableCustomColor"], outputs: ["change"] }, { kind: "component", type: PraxisMetaEditorImageComponent, selector: "praxis-meta-editor-image", inputs: ["model"], outputs: ["change"] }, { kind: "component", type: PraxisListJsonConfigEditorComponent, selector: "praxis-list-json-config-editor", inputs: ["document"], outputs: ["documentChange", "validationChange", "editorEvent"] }, { kind: "component", type: PdxColorPickerComponent, selector: "pdx-color-picker", inputs: ["actionsLayout", "activeView", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "clearButton", "disabledMode", "readonlyMode", "visible", "presentationMode", "fillMode", "format", "gradientSettings", "icon", "iconClass", "svgIcon", "paletteSettings", "popupSettings", "preview", "rounded", "size", "tabindex", "views", "maxRecent", "showRecent"], outputs: ["valueChange", "open", "close", "cancel", "activeViewChange", "activeColorClick", "focusEvent", "blurEvent"] }, { kind: "component", type: SurfaceOpenActionEditorComponent, selector: "praxis-surface-open-action-editor", inputs: ["value", "hostKind"], outputs: ["valueChange"] }] });
|
|
7404
7412
|
}
|
|
7405
7413
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisListConfigEditor, decorators: [{
|
|
7406
7414
|
type: Component,
|
|
@@ -12610,6 +12618,7 @@ class PraxisList {
|
|
|
12610
12618
|
return this.config?.layout?.lines ?? 2;
|
|
12611
12619
|
}
|
|
12612
12620
|
boundControl = new FormControl(null);
|
|
12621
|
+
selectionControlSubscription;
|
|
12613
12622
|
skinScopeId = `praxis-list-scope-${PraxisList.nextSkinScopeId++}`;
|
|
12614
12623
|
skinClasses = '';
|
|
12615
12624
|
inlineCss = '';
|
|
@@ -12755,6 +12764,7 @@ class PraxisList {
|
|
|
12755
12764
|
ngOnDestroy() {
|
|
12756
12765
|
this.assistantSessions.removeContextSession(this.buildAiAssistantContextSnapshot());
|
|
12757
12766
|
this.aiAssistantStateSubscription?.unsubscribe();
|
|
12767
|
+
this.selectionControlSubscription?.unsubscribe();
|
|
12758
12768
|
this.destroy$.next();
|
|
12759
12769
|
this.destroy$.complete();
|
|
12760
12770
|
}
|
|
@@ -12853,9 +12863,11 @@ class PraxisList {
|
|
|
12853
12863
|
return (this.config?.selection?.mode || 'none') !== 'none';
|
|
12854
12864
|
}
|
|
12855
12865
|
setupSelectionBinding() {
|
|
12866
|
+
this.selectionControlSubscription?.unsubscribe();
|
|
12856
12867
|
if (!this.isSelectionEnabled()) {
|
|
12857
12868
|
// Keep a local control to satisfy template typing; it won't be used when selection is disabled
|
|
12858
12869
|
this.boundControl.setValue(null);
|
|
12870
|
+
this.watchSelectionControl();
|
|
12859
12871
|
return;
|
|
12860
12872
|
}
|
|
12861
12873
|
const mode = this.config?.selection?.mode || 'none';
|
|
@@ -12869,11 +12881,18 @@ class PraxisList {
|
|
|
12869
12881
|
: null;
|
|
12870
12882
|
if (ctrl instanceof FormControl) {
|
|
12871
12883
|
this.boundControl = ctrl;
|
|
12884
|
+
this.watchSelectionControl();
|
|
12872
12885
|
return;
|
|
12873
12886
|
}
|
|
12874
12887
|
}
|
|
12875
12888
|
// Fallback to local control
|
|
12876
12889
|
this.boundControl = new FormControl(mode === 'multiple' ? [] : null);
|
|
12890
|
+
this.watchSelectionControl();
|
|
12891
|
+
}
|
|
12892
|
+
watchSelectionControl() {
|
|
12893
|
+
this.selectionControlSubscription = this.boundControl.valueChanges
|
|
12894
|
+
.pipe(takeUntil$1(this.destroy$))
|
|
12895
|
+
.subscribe(() => this.cdr.markForCheck());
|
|
12877
12896
|
}
|
|
12878
12897
|
leading = (item) => this.evalSlot('leading', item);
|
|
12879
12898
|
primary = (item) => this.evalSlot('primary', item);
|
|
@@ -13583,12 +13602,6 @@ class PraxisList {
|
|
|
13583
13602
|
}
|
|
13584
13603
|
this.onItemClick(item, index, section);
|
|
13585
13604
|
}
|
|
13586
|
-
onRowSpaceActivate(event, clickable, item, index, section) {
|
|
13587
|
-
if (!clickable)
|
|
13588
|
-
return;
|
|
13589
|
-
event.preventDefault();
|
|
13590
|
-
this.onRowActivate(item, index, section);
|
|
13591
|
-
}
|
|
13592
13605
|
onExpandToggle(event, item, index) {
|
|
13593
13606
|
event.preventDefault();
|
|
13594
13607
|
event.stopPropagation();
|
|
@@ -13617,6 +13630,51 @@ class PraxisList {
|
|
|
13617
13630
|
}
|
|
13618
13631
|
return this.t('emptyState', 'Nenhum item disponível');
|
|
13619
13632
|
}
|
|
13633
|
+
selectionListAriaLabel() {
|
|
13634
|
+
if (this.config?.a11y?.ariaLabelledBy) {
|
|
13635
|
+
return this.config.a11y.ariaLabel || null;
|
|
13636
|
+
}
|
|
13637
|
+
return (this.config?.a11y?.ariaLabel ||
|
|
13638
|
+
this.t('selection.ariaLabel', 'Selectable list'));
|
|
13639
|
+
}
|
|
13640
|
+
selectionControlName() {
|
|
13641
|
+
return `${this.skinScopeId}-selection`;
|
|
13642
|
+
}
|
|
13643
|
+
selectionControlAriaLabel(item) {
|
|
13644
|
+
return `${this.t('selection.selectItem', 'Select item')}: ${this.accessibleItemLabel(item)}`;
|
|
13645
|
+
}
|
|
13646
|
+
isItemSelected(item) {
|
|
13647
|
+
return this.normalizeListSelectionValue(this.boundControl?.value).some((selected) => this.selectionItemsEqual(selected, item));
|
|
13648
|
+
}
|
|
13649
|
+
setItemSelected(item, selected) {
|
|
13650
|
+
if (!this.isSelectionEnabled() || this.boundControl.disabled)
|
|
13651
|
+
return;
|
|
13652
|
+
const current = this.normalizeListSelectionValue(this.boundControl.value);
|
|
13653
|
+
const mode = this.config?.selection?.mode || 'none';
|
|
13654
|
+
let next;
|
|
13655
|
+
if (mode === 'multiple') {
|
|
13656
|
+
next = selected
|
|
13657
|
+
? this.isItemSelected(item)
|
|
13658
|
+
? current
|
|
13659
|
+
: [...current, item]
|
|
13660
|
+
: current.filter((candidate) => !this.selectionItemsEqual(candidate, item));
|
|
13661
|
+
}
|
|
13662
|
+
else {
|
|
13663
|
+
if (!selected)
|
|
13664
|
+
return;
|
|
13665
|
+
next = item;
|
|
13666
|
+
}
|
|
13667
|
+
this.boundControl.setValue(next);
|
|
13668
|
+
this.boundControl.markAsDirty();
|
|
13669
|
+
this.boundControl.markAsTouched();
|
|
13670
|
+
this.onSelectionChange();
|
|
13671
|
+
}
|
|
13672
|
+
selectionItemsEqual(left, right) {
|
|
13673
|
+
const compareBy = this.config?.selection?.compareBy;
|
|
13674
|
+
if (!compareBy)
|
|
13675
|
+
return left === right;
|
|
13676
|
+
return this.lookup(left, compareBy) === this.lookup(right, compareBy);
|
|
13677
|
+
}
|
|
13620
13678
|
isResourceConnectionMissing() {
|
|
13621
13679
|
const dataSource = this.config?.dataSource;
|
|
13622
13680
|
const hasLocalData = Array.isArray(dataSource?.data) && dataSource.data.length > 0;
|
|
@@ -13625,6 +13683,7 @@ class PraxisList {
|
|
|
13625
13683
|
}
|
|
13626
13684
|
async onActionClick(ev, actionId, item, index) {
|
|
13627
13685
|
ev.stopPropagation();
|
|
13686
|
+
const focusOrigin = this.resolveActionFocusOrigin(ev);
|
|
13628
13687
|
const action = (this.config?.actions || []).find((a) => a.id === actionId);
|
|
13629
13688
|
if (action?.confirmation) {
|
|
13630
13689
|
const confirmed = this.dialog?.confirm
|
|
@@ -13647,7 +13706,7 @@ class PraxisList {
|
|
|
13647
13706
|
this.actionLoadingState[loadingKey] = true;
|
|
13648
13707
|
try {
|
|
13649
13708
|
if (recordOpenIntent) {
|
|
13650
|
-
await this.executeRecordOpenAction(actionId, recordOpenReference, item);
|
|
13709
|
+
await this.executeRecordOpenAction(actionId, recordOpenReference, item, focusOrigin);
|
|
13651
13710
|
}
|
|
13652
13711
|
else if (globalAction?.actionId) {
|
|
13653
13712
|
const hasConfiguredPayload = Object.prototype.hasOwnProperty.call(globalAction, 'payload');
|
|
@@ -13664,7 +13723,11 @@ class PraxisList {
|
|
|
13664
13723
|
sourceId: this.listId,
|
|
13665
13724
|
output: 'actionClick',
|
|
13666
13725
|
payload,
|
|
13667
|
-
runtime: {
|
|
13726
|
+
runtime: {
|
|
13727
|
+
item,
|
|
13728
|
+
injector: this.injector,
|
|
13729
|
+
...(focusOrigin ? { focusOrigin } : {}),
|
|
13730
|
+
},
|
|
13668
13731
|
meta: { actionId },
|
|
13669
13732
|
});
|
|
13670
13733
|
if (result && !result.success && isDevMode()) {
|
|
@@ -13699,7 +13762,7 @@ class PraxisList {
|
|
|
13699
13762
|
this.actionClick.emit({ actionId, item, index });
|
|
13700
13763
|
}
|
|
13701
13764
|
}
|
|
13702
|
-
async executeRecordOpenAction(actionId, reference, item) {
|
|
13765
|
+
async executeRecordOpenAction(actionId, reference, item, focusOrigin) {
|
|
13703
13766
|
if (!reference) {
|
|
13704
13767
|
throw new ResourceRecordOpenError('INVALID_REFERENCE', 'List record-open action is missing its governed reference.');
|
|
13705
13768
|
}
|
|
@@ -13715,7 +13778,11 @@ class PraxisList {
|
|
|
13715
13778
|
sourceId: this.listId,
|
|
13716
13779
|
output: 'actionClick',
|
|
13717
13780
|
payload: resolution.payload,
|
|
13718
|
-
runtime: {
|
|
13781
|
+
runtime: {
|
|
13782
|
+
item,
|
|
13783
|
+
injector: this.injector,
|
|
13784
|
+
...(focusOrigin ? { focusOrigin } : {}),
|
|
13785
|
+
},
|
|
13719
13786
|
meta: {
|
|
13720
13787
|
actionId,
|
|
13721
13788
|
resourceKey: reference.target.resourceKey,
|
|
@@ -13731,6 +13798,14 @@ class PraxisList {
|
|
|
13731
13798
|
resolveRecordOpenService() {
|
|
13732
13799
|
return this.injector.get(ResourceRecordOpenService);
|
|
13733
13800
|
}
|
|
13801
|
+
resolveActionFocusOrigin(event) {
|
|
13802
|
+
if (typeof HTMLElement === 'undefined')
|
|
13803
|
+
return undefined;
|
|
13804
|
+
const origin = event.currentTarget;
|
|
13805
|
+
return origin instanceof HTMLElement && typeof origin.focus === 'function'
|
|
13806
|
+
? origin
|
|
13807
|
+
: undefined;
|
|
13808
|
+
}
|
|
13734
13809
|
reportRecordOpenFailure(actionId, reference, error) {
|
|
13735
13810
|
const failureCode = error instanceof ResourceRecordOpenError
|
|
13736
13811
|
? error.code
|
|
@@ -13814,7 +13889,7 @@ class PraxisList {
|
|
|
13814
13889
|
lookup(ctx, path) {
|
|
13815
13890
|
return path.split('.').reduce((acc, key) => acc?.[key], ctx);
|
|
13816
13891
|
}
|
|
13817
|
-
onSelectionChange(
|
|
13892
|
+
onSelectionChange() {
|
|
13818
13893
|
if (!this.boundControl)
|
|
13819
13894
|
return;
|
|
13820
13895
|
const val = this.boundControl.value;
|
|
@@ -15569,7 +15644,7 @@ class PraxisList {
|
|
|
15569
15644
|
ListDataService,
|
|
15570
15645
|
MatPaginatorIntl,
|
|
15571
15646
|
providePraxisListI18n(),
|
|
15572
|
-
], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"praxis-list-root\"\n [ngClass]=\"skinClasses\"\n [attr.data-skin-scope]=\"skinScopeId\"\n [attr.aria-label]=\"config.a11y?.ariaLabel || null\"\n [attr.aria-labelledby]=\"config.a11y?.ariaLabelledBy || null\"\n>\n @if (inlineCss) {\n <style [attr.nonce]=\"cspNonce || null\" [textContent]=\"inlineCss\"></style>\n }\n\n @if (enableCustomization) {\n <div class=\"list-assistant\">\n <button\n mat-mini-fab\n type=\"button\"\n color=\"primary\"\n (click)=\"openConfigEditor()\"\n [matTooltip]=\"configEditorLabel()\"\n [attr.aria-label]=\"configEditorLabel()\"\n data-testid=\"praxis-list-open-config-editor\"\n >\n <mat-icon [praxisIcon]=\"'edit'\"></mat-icon>\n </button>\n <button\n mat-mini-fab\n type=\"button\"\n color=\"primary\"\n (click)=\"openAiAssistant()\"\n matTooltip=\"Copiloto semantico Praxis\"\n aria-label=\"Abrir copiloto semantico Praxis da lista\"\n data-testid=\"praxis-list-ai-assistant-trigger\"\n >\n <mat-icon [praxisIcon]=\"'auto_awesome'\"></mat-icon>\n </button>\n </div>\n }\n\n @if (aiAssistantOpen && aiAssistantViewState) {\n <praxis-ai-assistant-shell\n [labels]=\"aiAssistantLabels\"\n [mode]=\"aiAssistantViewState.mode\"\n [state]=\"aiAssistantViewState.state\"\n [contextItems]=\"aiAssistantViewState.contextItems\"\n [attachments]=\"aiAssistantViewState.attachments\"\n [messages]=\"aiAssistantViewState.messages\"\n [quickReplies]=\"aiAssistantViewState.quickReplies\"\n [prompt]=\"aiAssistantPrompt\"\n [statusText]=\"aiAssistantViewState.statusText\"\n [errorText]=\"aiAssistantViewState.errorText\"\n [busy]=\"\n aiAssistantViewState.state === 'processing' ||\n aiAssistantViewState.state === 'applying'\n \"\n [canApply]=\"aiAssistantViewState.canApply\"\n [layout]=\"aiAssistantLayout\"\n testIdPrefix=\"praxis-list-ai-assistant\"\n panelTestId=\"praxis-list-ai-assistant-panel\"\n submitTestId=\"praxis-list-ai-assistant-submit\"\n applyTestId=\"praxis-list-ai-assistant-apply\"\n (promptChange)=\"onAiAssistantPromptChange($event)\"\n (submitPrompt)=\"onAiAssistantSubmit($event)\"\n (apply)=\"onAiAssistantApply()\"\n (retryTurn)=\"onAiAssistantRetry()\"\n (cancelTurn)=\"onAiAssistantCancel()\"\n (quickReply)=\"onAiAssistantQuickReply($event)\"\n (editMessage)=\"onAiAssistantEditMessage($event)\"\n (resendMessage)=\"onAiAssistantResendMessage($event)\"\n (layoutChange)=\"onAiAssistantLayoutChange($event)\"\n (close)=\"closeAiAssistant()\"\n ></praxis-ai-assistant-shell>\n }\n\n <!-- Skeleton while loading -->\n @if ((loading$ | async) && hasSkeleton()) {\n @if (isListVariant()) {\n <mat-list>\n @for (_ of skeletonItems(); track $index; let i = $index) {\n <mat-list-item>\n <div class=\"list-item-content\">\n <div class=\"list-item-leading\">\n <div class=\"skeleton skeleton-avatar\"></div>\n </div>\n <div class=\"list-item-text\">\n <div class=\"skeleton skeleton-line w-60\"></div>\n @if (layoutLines > 1) {\n <div class=\"skeleton skeleton-line w-40\"></div>\n }\n </div>\n <div class=\"list-item-trailing\">\n <div class=\"skeleton skeleton-chip\"></div>\n </div>\n </div>\n </mat-list-item>\n }\n </mat-list>\n } @else {\n <div class=\"cards-grid\">\n @for (_ of skeletonItems(); track $index; let i = $index) {\n <div class=\"item-card\">\n <div class=\"list-item-content\">\n <div class=\"list-item-leading\">\n <div class=\"skeleton skeleton-avatar\"></div>\n </div>\n <div class=\"list-item-text\">\n <div class=\"skeleton skeleton-line w-60\"></div>\n @if (layoutLines > 1) {\n <div class=\"skeleton skeleton-line w-40\"></div>\n }\n </div>\n <div class=\"list-item-trailing\">\n <div class=\"skeleton skeleton-chip\"></div>\n </div>\n </div>\n </div>\n }\n </div>\n }\n } @else {\n <ng-container *ngTemplateOutlet=\"notLoading\"></ng-container>\n }\n\n <ng-template #notLoading>\n @if (\n config.export?.enabled ||\n (isRemoteDataMode() &&\n (config.ui?.showSort ||\n (config.ui?.showSearch && config.ui?.searchField)))\n ) {\n <div\n class=\"list-collection-toolbar\"\n role=\"group\"\n [attr.aria-label]=\"t('toolbar.label', 'Controles da lista')\"\n >\n @if (\n isRemoteDataMode() && config.ui?.showSearch && config.ui?.searchField\n ) {\n <mat-form-field\n class=\"list-toolbar-search\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n >\n <mat-label>{{\n config.ui?.searchPlaceholder || t(\"toolbar.search\", \"Buscar\")\n }}</mat-label>\n <mat-icon matPrefix [praxisIcon]=\"'search'\"></mat-icon>\n <input\n matInput\n type=\"search\"\n (input)=\"onSearchInput($any($event.target).value)\"\n />\n </mat-form-field>\n }\n\n <div class=\"list-toolbar-commands\">\n @if (isRemoteDataMode() && config.ui?.showSort) {\n <mat-form-field\n class=\"list-toolbar-sort\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n >\n <mat-label>{{ t(\"toolbar.sort\", \"Ordenar por\") }}</mat-label>\n @let activeSort = activeSortValue((page$ | async)?.sort);\n <mat-select\n [value]=\"activeSort\"\n (selectionChange)=\"onSortChange($event.value)\"\n >\n @if (activeSort && !hasSortOption(activeSort)) {\n <mat-option [value]=\"activeSort\">{{ activeSort }}</mat-option>\n }\n @for (op of config.ui?.sortOptions || []; track $index) {\n <mat-option [value]=\"sortOptionValue(op)\">{{\n sortOptionLabel(op)\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n\n @if (config.export?.enabled) {\n <button\n mat-stroked-button\n type=\"button\"\n [matMenuTriggerFor]=\"listExportMenu\"\n [attr.aria-label]=\"t('export.button', 'Exportar')\"\n [disabled]=\"exportBusy\"\n [attr.aria-busy]=\"exportBusy\"\n >\n <mat-icon [praxisIcon]=\"'download'\"></mat-icon>\n {{ t(\"export.button\", \"Exportar\") }}\n </button>\n <span class=\"cdk-visually-hidden\" aria-live=\"polite\">\n {{ exportStatusMessage }}\n </span>\n <mat-menu #listExportMenu=\"matMenu\">\n @for (format of listExportFormats(); track format) {\n <button\n mat-menu-item\n type=\"button\"\n (click)=\"onExportAction(format)\"\n >\n <mat-icon [praxisIcon]=\"exportIcon(format)\"></mat-icon>\n {{ format.toUpperCase() }}\n </button>\n }\n </mat-menu>\n }\n </div>\n </div>\n }\n\n @if (error$ | async; as remoteError) {\n <div class=\"list-remote-error\" role=\"alert\">\n <mat-icon [praxisIcon]=\"'error_outline'\"></mat-icon>\n <div class=\"list-remote-error__copy\">\n <strong>{{\n t(\"remoteError.title\", \"N\u00E3o foi poss\u00EDvel carregar a lista\")\n }}</strong>\n <span>\n {{\n remoteError.filtered\n ? t(\n \"remoteError.filteredDescription\",\n \"Os filtros permanecem aplicados. Nenhum dado sem filtro foi exibido.\"\n )\n : t(\n \"remoteError.description\",\n \"Verifique a conex\u00E3o e tente novamente.\"\n )\n }}\n </span>\n </div>\n <button mat-stroked-button type=\"button\" (click)=\"retryRemoteData()\">\n {{ t(\"remoteError.retry\", \"Tentar novamente\") }}\n </button>\n </div>\n }\n\n <!-- Empty state -->\n @if (items$ | async; as all) {\n @if (all.length === 0 && !(error$ | async)) {\n <div\n class=\"section-header\"\n [class.section-header--setup]=\"isResourceConnectionMissing()\"\n >\n @if (emptyStateTemplate(); as empty) {\n @if (\n simpleRichContentNodes(empty, {\n imageAlt: config.templating?.emptyState?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content [nodes]=\"richNodes\"></praxis-rich-content>\n } @else {\n @switch (empty.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"empty.value\"\n [ngClass]=\"empty.class\"\n [color]=\"\n isThemeColor(empty.color) ? empty.color : undefined\n \"\n [style.cssText]=\"\n (empty.style ? empty.style + ';' : '') +\n iconStyle(empty.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <span\n class=\"inline-image\"\n [ngClass]=\"empty.class\"\n [style.cssText]=\"empty.style\"\n >\n <img\n [src]=\"empty.value\"\n [alt]=\"config.templating?.emptyState?.imageAlt || ''\"\n />\n @if (empty.badge?.value) {\n <mat-chip\n class=\"inline-badge\"\n [color]=\"\n isThemeColor(empty.badge?.color)\n ? empty.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (empty.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(empty.badge?.color, empty.badge?.variant)\n \"\n >{{ empty.badge?.value }}</mat-chip\n >\n }\n </span>\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(empty.color) ? empty.color : undefined\n \"\n [ngClass]=\"[\n empty.class || '',\n (empty.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : '',\n ]\"\n [style.cssText]=\"\n chipStyle(empty.color, empty.variant) +\n (empty.style ? ';' + empty.style : '')\n \"\n >{{ empty.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n <span [ngClass]=\"empty.class\" [style.cssText]=\"empty.style\">\n @for (\n _ of ratingRange(empty);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, empty.value)\"\n [color]=\"ratingThemeColor(empty)\"\n [style.cssText]=\"ratingIconStyle(empty)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"empty.class\"\n [style.cssText]=\"empty.style\"\n [innerHTML]=\"empty.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"empty\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose [compose]=\"empty\"></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"empty\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span [ngClass]=\"empty.class\" [style.cssText]=\"empty.style\">{{\n empty.value\n }}</span>\n }\n }\n }\n }\n </div>\n }\n }\n\n <ng-template\n #rowLayoutNode\n let-node\n let-slot=\"slot\"\n let-imageAlt=\"imageAlt\"\n >\n @if (\n simpleRichContentNodes(node, { slot: slot, imageAlt: imageAlt });\n as richNodes\n ) {\n <praxis-rich-content [nodes]=\"richNodes\"></praxis-rich-content>\n } @else {\n @switch (node?.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"node?.value\"\n [ngClass]=\"node?.class\"\n [color]=\"isThemeColor(node?.color) ? node?.color : undefined\"\n [style.cssText]=\"\n (node?.style || '' ? node.style + ';' : '') +\n iconStyle(node?.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <span\n class=\"inline-image\"\n [ngClass]=\"node?.class\"\n [style.cssText]=\"node?.style\"\n >\n <img\n [src]=\"node?.value\"\n [alt]=\"node?.imageAlt || imageAlt || ''\"\n />\n @if (node?.badge?.value) {\n <mat-chip\n class=\"inline-badge\"\n [color]=\"\n isThemeColor(node?.badge?.color)\n ? node?.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (node?.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(node?.badge?.color, node?.badge?.variant)\n \"\n >\n {{ node?.badge?.value }}\n </mat-chip>\n }\n </span>\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"isThemeColor(node?.color) ? node?.color : undefined\"\n [ngClass]=\"[\n node?.class || '',\n (node?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : '',\n ]\"\n [style.cssText]=\"\n chipStyle(node?.color, node?.variant) +\n (node?.style ? ';' + node.style : '')\n \"\n >\n {{ node?.value }}\n </mat-chip>\n }\n @case (\"rating\") {\n <span [ngClass]=\"node?.class\" [style.cssText]=\"node?.style\">\n @for (_ of ratingRange(node); track $index; let idx = $index) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, node?.value)\"\n [color]=\"ratingThemeColor(node)\"\n [style.cssText]=\"ratingIconStyle(node)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"node?.class\"\n [style.cssText]=\"node?.style\"\n [innerHTML]=\"node?.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"node\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose [compose]=\"node\"></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"node\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span [ngClass]=\"node?.class\" [style.cssText]=\"node?.style\">\n @if (\n slot === \"meta\" && config.templating?.metaPrefixIcon;\n as mpi\n ) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n {{ node?.value }}\n </span>\n }\n }\n }\n </ng-template>\n\n <ng-template #featureContent let-item let-feature=\"feature\">\n @if (featureTemplateNode(item, feature); as featureNode) {\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: featureNode,\n slot: 'feature',\n imageAlt: feature?.imageAlt || '',\n }\n \"\n ></ng-container>\n } @else if (featureRichContentNodes(item, feature); as featureNodes) {\n <praxis-rich-content [nodes]=\"featureNodes\"></praxis-rich-content>\n } @else {\n @if (feature.icon && featuresMode() !== \"labels-only\") {\n <mat-icon [praxisIcon]=\"feature.icon\"></mat-icon>\n }\n @if (featuresMode() !== \"icons-only\") {\n <span [ngClass]=\"feature.class\" [style.cssText]=\"feature.style\">\n @for (line of featureLabelLines(item, feature.expr); track $index) {\n <span class=\"feature-line\">{{ line }}</span>\n }\n </span>\n }\n }\n @if (\n featureProgressPercent(item, feature.expr, feature.class);\n as progress\n ) {\n <span class=\"feature-progress\" aria-hidden=\"true\">\n <span\n class=\"feature-progress__value\"\n [style.width.%]=\"progress\"\n ></span>\n </span>\n }\n </ng-template>\n\n <ng-template\n #listRowLayout\n let-item\n let-index=\"index\"\n let-sectionKey=\"sectionKey\"\n let-clickable=\"clickable\"\n >\n <div\n class=\"list-item-content\"\n [ngClass]=\"rowLayoutItemClass(item)\"\n [attr.style]=\"rowLayoutItemStyle(item)\"\n [attr.role]=\"clickable ? 'button' : null\"\n [attr.tabindex]=\"clickable ? '0' : null\"\n [attr.aria-label]=\"clickable ? itemAriaLabel(item) : null\"\n [attr.aria-expanded]=\"\n clickable && expansionOwnedByRow() ? isExpanded(item, index) : null\n \"\n [attr.aria-controls]=\"\n clickable && expansionOwnedByRow()\n ? expandRegionId(item, index)\n : null\n \"\n (click)=\"clickable ? onRowActivate(item, index, sectionKey) : null\"\n (keydown.enter)=\"\n clickable ? onRowActivate(item, index, sectionKey) : null\n \"\n (keydown.space)=\"\n onRowSpaceActivate($event, clickable, item, index, sectionKey)\n \"\n >\n @for (\n column of rowLayoutColumns();\n track rowLayoutTrackColumn($index, column)\n ) {\n <div\n [attr.data-row-slot]=\"column.slot\"\n [ngClass]=\"rowLayoutColumnClass(column)\"\n [attr.style]=\"rowLayoutColumnStyle(column)\"\n >\n @if (column.slot === \"leading\") {\n @if (rowLayoutSlot(item, column.slot); as lead) {\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: lead,\n slot: column.slot,\n imageAlt: rowLayoutImageAlt(column.slot),\n }\n \"\n ></ng-container>\n } @else {\n <span class=\"leading-placeholder\"></span>\n }\n } @else if (column.slot === \"trailing\") {\n @if (rowLayoutSlot(item, column.slot); as trailingNode) {\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: trailingNode,\n slot: column.slot,\n imageAlt: rowLayoutImageAlt(column.slot),\n }\n \"\n ></ng-container>\n }\n @if (showExpandIcon(\"trailing\")) {\n <button\n type=\"button\"\n [praxisIconButton]=\"\n isExpanded(item, index) ? 'expand_less' : 'expand_more'\n \"\n size=\"compact\"\n class=\"expand-toggle\"\n [attr.aria-label]=\"expandToggleAriaLabel(item, index)\"\n [attr.aria-expanded]=\"\n expansionOwnedByIcon() ? isExpanded(item, index) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByIcon() ? expandRegionId(item, index) : null\n \"\n (click)=\"onExpandToggle($event, item, index)\"\n ></button>\n }\n @if ((visibleActions(item, \"trailing\") || []).length) {\n <div\n class=\"list-item-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(item, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, item, index)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"onActionClick($event, action.id, item, index)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'stroked')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'raised')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant || action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'flat')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n } @else if (column.slot === \"actions\") {\n @if ((visibleActions(item, \"actions\") || []).length) {\n <div\n class=\"list-item-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(item, \"actions\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, item, index)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"onActionClick($event, action.id, item, index)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'stroked')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'raised')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant || action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'flat')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n } @else if (column.slot === \"expand\") {\n @if (showExpandIcon(\"expand\")) {\n <button\n type=\"button\"\n [praxisIconButton]=\"\n isExpanded(item, index) ? 'expand_less' : 'expand_more'\n \"\n size=\"compact\"\n class=\"expand-toggle\"\n [attr.aria-label]=\"expandToggleAriaLabel(item, index)\"\n [attr.aria-expanded]=\"\n expansionOwnedByIcon() ? isExpanded(item, index) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByIcon() ? expandRegionId(item, index) : null\n \"\n (click)=\"onExpandToggle($event, item, index)\"\n ></button>\n }\n } @else if (rowLayoutSlot(item, column.slot); as slotNode) {\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: slotNode,\n slot: column.slot,\n imageAlt: rowLayoutImageAlt(column.slot),\n }\n \"\n ></ng-container>\n }\n </div>\n }\n </div>\n </ng-template>\n\n <!-- List variant -->\n @if (isListVariant()) {\n <!-- Selection list -->\n @if (isSelectionEnabled()) {\n <mat-selection-list\n [multiple]=\"config.selection?.mode === 'multiple'\"\n [formControl]=\"boundControl\"\n (selectionChange)=\"onSelectionChange($event)\"\n >\n @for (\n section of sections$ | async;\n track trackBySection($index, section)\n ) {\n @if (section.key) {\n <div class=\"section-header mat-subheader\">\n @if (sectionHeaderTemplate(section.key); as sh) {\n @if (\n simpleRichContentNodes(sh, {\n imageAlt:\n config.templating?.sectionHeader?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (sh.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"sh.value\"\n [ngClass]=\"sh.class\"\n [color]=\"\n isThemeColor(sh.color) ? sh.color : undefined\n \"\n [style.cssText]=\"\n (sh.style ? sh.style + ';' : '') +\n iconStyle(sh.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <span\n class=\"inline-image\"\n [ngClass]=\"sh.class\"\n [style.cssText]=\"sh.style\"\n >\n <img\n [src]=\"sh.value\"\n [alt]=\"\n config.templating?.sectionHeader?.imageAlt || ''\n \"\n />\n @if (sh.badge?.value) {\n <mat-chip\n class=\"inline-badge\"\n [color]=\"\n isThemeColor(sh.badge?.color)\n ? sh.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (sh.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(sh.badge?.color, sh.badge?.variant)\n \"\n >{{ sh.badge?.value }}</mat-chip\n >\n }\n </span>\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(sh.color) ? sh.color : undefined\n \"\n [ngClass]=\"[\n sh.class || '',\n (sh.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : '',\n ]\"\n [style.cssText]=\"\n chipStyle(sh.color, sh.variant) +\n (sh.style ? ';' + sh.style : '')\n \"\n >{{ sh.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n <span [ngClass]=\"sh.class\" [style.cssText]=\"sh.style\">\n @for (\n _ of ratingRange(sh);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, sh.value)\"\n [color]=\"ratingThemeColor(sh)\"\n [style.cssText]=\"ratingIconStyle(sh)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"sh.class\"\n [style.cssText]=\"sh.style\"\n [innerHTML]=\"sh.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"sh\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"sh\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"sh\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span [ngClass]=\"sh.class\" [style.cssText]=\"sh.style\">{{\n sh.value\n }}</span>\n }\n }\n }\n }\n </div>\n }\n @for (\n item of section.items;\n track trackByItem($index, item);\n let i = $index\n ) {\n <mat-list-option\n [value]=\"item\"\n [attr.aria-label]=\"itemAriaLabel(item)\"\n >\n @if (hasRowLayoutGrid()) {\n <ng-container\n *ngTemplateOutlet=\"\n listRowLayout;\n context: {\n $implicit: item,\n index: i,\n sectionKey: section.key || undefined,\n clickable: false,\n }\n \"\n ></ng-container>\n } @else {\n <div\n class=\"list-item-content\"\n [ngClass]=\"itemRuleClass(item)\"\n [attr.style]=\"itemRuleStyle(item)\"\n >\n <div class=\"list-item-leading\">\n @if (leading(item); as lead) {\n @if (\n simpleRichContentNodes(lead, {\n imageAlt:\n config.templating?.leading?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"lead.value\"\n [ngClass]=\"lead.class\"\n [color]=\"\n isThemeColor(lead.color)\n ? lead.color\n : undefined\n \"\n [style.cssText]=\"\n (lead.style ? lead.style + ';' : '') +\n iconStyle(lead.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >\n <img\n [src]=\"lead.value\"\n [alt]=\"\n config.templating?.leading?.imageAlt || ''\n \"\n />\n @if (lead.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(lead.badge?.color)\n ? lead.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (lead.badge?.variant || 'filled') ===\n 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n lead.badge?.color,\n lead.badge?.variant\n )\n \"\n >{{ lead.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"text\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(lead.color)\n ? lead.color\n : undefined\n \"\n [ngClass]=\"\n (lead.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(lead.color, lead.variant)\n \"\n >{{ lead.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >\n @for (\n _ of ratingRange(lead);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, lead.value)\"\n [color]=\"ratingThemeColor(lead)\"\n [style.cssText]=\"ratingIconStyle(lead)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n [innerHTML]=\"lead.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"lead\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"lead\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"lead\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n }\n }\n } @else {\n <span class=\"leading-placeholder\"></span>\n }\n </div>\n <div class=\"list-item-text\">\n <div\n class=\"primary\"\n [ngClass]=\"primary(item)?.class\"\n [style.cssText]=\"primary(item)?.style\"\n >\n @if (\n simpleRichContentNodes(primary(item));\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (primary(item)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"primary(item)\"\n ></praxis-list-metric>\n } @else if (primary(item)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"primary(item)\"\n ></praxis-list-compose>\n } @else if (primary(item)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"primary(item)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ primary(item)?.value }}\n }\n </div>\n @if (layoutLines > 1) {\n <div\n class=\"secondary\"\n [ngClass]=\"secondary(item)?.class\"\n [style.cssText]=\"secondary(item)?.style\"\n >\n @if (\n simpleRichContentNodes(secondary(item));\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (secondary(item)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"secondary(item)\"\n ></praxis-list-metric>\n } @else if (secondary(item)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"secondary(item)\"\n ></praxis-list-compose>\n } @else if (secondary(item)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"secondary(item)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ secondary(item)?.value }}\n }\n </div>\n }\n @if (meta(item); as m) {\n @if (\n (config.templating?.metaPlacement === \"line\" &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")) ||\n (layoutLines > 2 &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\"))\n ) {\n <div\n class=\"tertiary\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (config.templating?.metaPrefixIcon; as mpi) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n {{ m.value }}\n </div>\n }\n }\n @if (featuresVisible()) {\n <div class=\"features\">\n @for (\n f of config.templating?.features || [];\n track $index;\n let fi = $index\n ) {\n <span\n class=\"feature\"\n [ngClass]=\"\n featureSemanticClass(item, f.expr, f.class)\n \"\n >\n <ng-container\n *ngTemplateOutlet=\"\n featureContent;\n context: { $implicit: item, feature: f }\n \"\n ></ng-container>\n </span>\n }\n </div>\n }\n </div>\n <div class=\"list-item-trailing\">\n @if (meta(item); as m) {\n @if (\n !(\n (config.templating?.metaPlacement === \"line\" ||\n layoutLines > 2) &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")\n )\n ) {\n <div\n class=\"meta\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (\n simpleRichContentNodes(m, { slot: \"meta\" });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(m.color)\n ? m.color\n : undefined\n \"\n [ngClass]=\"\n (m.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(m.color, m.variant)\n \"\n >{{ m.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(m);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, m.value)\"\n [color]=\"ratingThemeColor(m)\"\n [style.cssText]=\"ratingIconStyle(m)\"\n ></mat-icon>\n }\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"m.value\"\n [color]=\"\n isThemeColor(m.color)\n ? m.color\n : undefined\n \"\n [style.cssText]=\"iconStyle(m.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img\n [src]=\"m.value\"\n [alt]=\"m.imageAlt || ''\"\n /></span>\n }\n @case (\"html\") {\n <span [innerHTML]=\"m.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"m\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"m\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"m\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>\n @if (\n config.templating?.metaPrefixIcon;\n as mpi2\n ) {\n <mat-icon [praxisIcon]=\"mpi2\"></mat-icon>\n }\n {{ m.value }}</span\n >\n }\n }\n }\n </div>\n }\n }\n @if (trailing(item); as tr) {\n <div\n class=\"trailing\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n @if (\n simpleRichContentNodes(tr, {\n imageAlt:\n config.templating?.trailing?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (tr.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr.color)\n ? tr.color\n : undefined\n \"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(tr.color, tr.variant)\n \"\n >{{ tr.value }}</mat-chip\n >\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"\n isThemeColor(tr.color)\n ? tr.color\n : undefined\n \"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n <img\n [src]=\"tr.value\"\n [alt]=\"\n config.templating?.trailing?.imageAlt ||\n ''\n \"\n />\n @if (tr.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(tr.badge?.color)\n ? tr.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (tr.badge?.variant || 'filled') ===\n 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n tr.badge?.color,\n tr.badge?.variant\n )\n \"\n >{{ tr.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(tr);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, tr.value)\"\n [color]=\"ratingThemeColor(tr)\"\n [style.cssText]=\"ratingIconStyle(tr)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n @if (\n item.alertsData !== undefined &&\n item.ownerName\n ) {\n <!-- Component-based rendering for executive trailing -->\n <span class=\"exec-trailing-shell\">\n <praxis-executive-alerts\n [alerts]=\"item.alertsData\"\n ></praxis-executive-alerts>\n <praxis-executive-owner\n [name]=\"item.ownerName\"\n ></praxis-executive-owner>\n </span>\n } @else {\n <span [innerHTML]=\"tr.value\"></span>\n }\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"tr\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"tr\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"tr\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>{{ tr.value }}</span>\n }\n }\n }\n </div>\n }\n @if (showExpandIcon(\"trailing\")) {\n <button\n type=\"button\"\n [praxisIconButton]=\"\n isExpanded(item, i) ? 'expand_less' : 'expand_more'\n \"\n size=\"compact\"\n class=\"expand-toggle\"\n [attr.aria-label]=\"expandToggleAriaLabel(item, i)\"\n [attr.aria-expanded]=\"\n expansionOwnedByIcon() ? isExpanded(item, i) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByIcon()\n ? expandRegionId(item, i)\n : null\n \"\n (click)=\"onExpandToggle($event, item, i)\"\n ></button>\n }\n @if ((visibleActions(item, \"trailing\") || []).length) {\n <div\n class=\"list-item-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(item, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, item, i)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'stroked')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'raised')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant ||\n action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'flat')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n </div>\n </div>\n }\n @if (isExpandable() && isExpanded(item, i)) {\n <div\n class=\"item-expansion\"\n [ngClass]=\"expansionRenderingClasses()\"\n [style]=\"expansionRenderingStyle()\"\n [id]=\"expandRegionId(item, i)\"\n role=\"region\"\n [attr.aria-label]=\"expansionRegionAriaLabel(item)\"\n >\n <div class=\"item-expansion-grid\">\n @for (\n expansionSection of expansionSections(item);\n track expansionSection.id\n ) {\n <section\n class=\"expansion-section\"\n [attr.data-section-type]=\"expansionSection.type\"\n >\n @if (expansionSection.title) {\n <div class=\"expansion-section-title\">\n {{ expansionSection.title }}\n </div>\n }\n @if (\n expansionSectionHasContent(\n item,\n expansionSection,\n i\n )\n ) {\n @switch (expansionSection.type) {\n @case (\"chip-list\") {\n <div class=\"expansion-chip-list\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <mat-chip class=\"expansion-chip\">{{\n expansionItemLabel(expansionEntry)\n }}</mat-chip>\n }\n </div>\n }\n @case (\"timeline\") {\n <div class=\"expansion-timeline\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-timeline-item\">\n <div class=\"expansion-timeline-title\">\n {{\n expansionTimelineTitle(expansionEntry)\n }}\n </div>\n @if (\n expansionTimelineMeta(expansionEntry)\n ) {\n <div class=\"expansion-timeline-meta\">\n {{\n expansionTimelineMeta(\n expansionEntry\n )\n }}\n </div>\n }\n @if (\n expansionTimelineDescription(\n expansionEntry\n )\n ) {\n <div\n class=\"expansion-timeline-description\"\n >\n {{\n expansionTimelineDescription(\n expansionEntry\n )\n }}\n </div>\n }\n </div>\n }\n </div>\n }\n @case (\"key-value\") {\n <dl class=\"expansion-key-value\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-key-value-row\">\n @if (\n expansionKeyValueKey(expansionEntry)\n ) {\n <dt>\n {{\n expansionKeyValueKey(expansionEntry)\n }}\n </dt>\n }\n <dd>\n {{\n expansionKeyValueValue(expansionEntry)\n }}\n </dd>\n </div>\n }\n </dl>\n }\n @case (\"rich-content\") {\n @if (\n expansionRichContentDocument(\n item,\n expansionSection,\n i\n );\n as richExpansionDocument\n ) {\n <praxis-rich-content\n [document]=\"richExpansionDocument\"\n [context]=\"\n expansionRichContentContext(\n item,\n expansionSection,\n i\n )\n \"\n [hostCapabilities]=\"\n expansionRichContentHostCapabilities(\n item,\n expansionSection,\n i\n )\n \"\n rootClassName=\"praxis-list-expansion-rich-content\"\n ></praxis-rich-content>\n }\n }\n @default {\n <div class=\"expansion-info-list\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-info-item\">\n <div class=\"expansion-info-title\">\n {{ expansionInfoTitle(expansionEntry) }}\n </div>\n @if (expansionInfoValue(expansionEntry)) {\n <div class=\"expansion-info-value\">\n {{\n expansionInfoValue(expansionEntry)\n }}\n </div>\n }\n </div>\n }\n </div>\n }\n }\n } @else {\n <div class=\"expansion-empty\">\n {{\n expansionSection.emptyLabel ||\n \"Sem dados dispon\u00EDveis\"\n }}\n </div>\n }\n </section>\n }\n </div>\n </div>\n }\n </mat-list-option>\n @if (\n (config.layout?.dividers === \"all\" ||\n config.layout?.dividers === \"between\") &&\n i < section.items.length - 1\n ) {\n <mat-divider></mat-divider>\n }\n }\n }\n </mat-selection-list>\n } @else {\n <ng-container *ngTemplateOutlet=\"readList\"></ng-container>\n }\n\n <!-- Read-only list -->\n <ng-template #readList>\n <mat-list>\n @for (\n section of sections$ | async;\n track trackBySection($index, section);\n let sidx = $index\n ) {\n @if (section.key) {\n <div class=\"section-header mat-subheader\">\n @if (sectionHeaderTemplate(section.key); as sh) {\n @if (\n simpleRichContentNodes(sh, {\n imageAlt:\n config.templating?.sectionHeader?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (sh.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"sh.value\"\n [ngClass]=\"sh.class\"\n [color]=\"\n isThemeColor(sh.color) ? sh.color : undefined\n \"\n [style.cssText]=\"\n (sh.style ? sh.style + ';' : '') +\n iconStyle(sh.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <span\n class=\"inline-image\"\n [ngClass]=\"sh.class\"\n [style.cssText]=\"sh.style\"\n >\n <img\n [src]=\"sh.value\"\n [alt]=\"\n config.templating?.sectionHeader?.imageAlt || ''\n \"\n />\n @if (sh.badge?.value) {\n <mat-chip\n class=\"inline-badge\"\n [color]=\"\n isThemeColor(sh.badge?.color)\n ? sh.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (sh.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(sh.badge?.color, sh.badge?.variant)\n \"\n >{{ sh.badge?.value }}</mat-chip\n >\n }\n </span>\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(sh.color) ? sh.color : undefined\n \"\n [ngClass]=\"[\n sh.class || '',\n (sh.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : '',\n ]\"\n [style.cssText]=\"\n chipStyle(sh.color, sh.variant) +\n (sh.style ? ';' + sh.style : '')\n \"\n >{{ sh.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n <span [ngClass]=\"sh.class\" [style.cssText]=\"sh.style\">\n @for (\n _ of ratingRange(sh);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, sh.value)\"\n [color]=\"ratingThemeColor(sh)\"\n [style.cssText]=\"ratingIconStyle(sh)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"sh.class\"\n [style.cssText]=\"sh.style\"\n [innerHTML]=\"sh.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"sh\"></praxis-list-metric>\n }\n @default {\n <span [ngClass]=\"sh.class\" [style.cssText]=\"sh.style\">{{\n sh.value\n }}</span>\n }\n }\n }\n }\n </div>\n }\n @for (\n item of section.items;\n track trackByItem($index, item);\n let i = $index\n ) {\n <mat-list-item [attr.data-item-status]=\"item?.status || null\">\n @if (hasRowLayoutGrid()) {\n <ng-container\n *ngTemplateOutlet=\"\n listRowLayout;\n context: {\n $implicit: item,\n index: i,\n sectionKey: section.key || undefined,\n clickable: true,\n }\n \"\n ></ng-container>\n } @else {\n <div\n class=\"list-item-content\"\n [ngClass]=\"itemRuleClass(item)\"\n [attr.style]=\"itemRuleStyle(item)\"\n >\n <button\n type=\"button\"\n class=\"list-item-main-action\"\n [attr.aria-label]=\"itemAriaLabel(item)\"\n [attr.aria-expanded]=\"\n expansionOwnedByRow() ? isExpanded(item, i) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByRow() ? expandRegionId(item, i) : null\n \"\n (click)=\"onRowActivate(item, i, section.key || undefined)\"\n >\n <div class=\"list-item-leading\">\n @if (leading(item); as lead) {\n @if (\n simpleRichContentNodes(lead, {\n imageAlt:\n config.templating?.leading?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"lead.value\"\n [ngClass]=\"lead.class\"\n [color]=\"\n isThemeColor(lead.color)\n ? lead.color\n : undefined\n \"\n [style.cssText]=\"\n (lead.style ? lead.style + ';' : '') +\n iconStyle(lead.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >\n <img\n [src]=\"lead.value\"\n [alt]=\"\n config.templating?.leading?.imageAlt || ''\n \"\n />\n @if (lead.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(lead.badge?.color)\n ? lead.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (lead.badge?.variant || 'filled') ===\n 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n lead.badge?.color,\n lead.badge?.variant\n )\n \"\n >{{ lead.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"text\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(lead.color)\n ? lead.color\n : undefined\n \"\n [ngClass]=\"\n (lead.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(lead.color, lead.variant)\n \"\n >{{ lead.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(lead);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, lead.value)\"\n [color]=\"ratingThemeColor(lead)\"\n [style.cssText]=\"ratingIconStyle(lead)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n [innerHTML]=\"lead.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"lead\"\n ></praxis-list-metric>\n }\n @default {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n }\n }\n } @else {\n <span class=\"leading-placeholder\"></span>\n }\n </div>\n <div class=\"list-item-text\">\n <div\n class=\"primary\"\n [ngClass]=\"primary(item)?.class\"\n [style.cssText]=\"primary(item)?.style\"\n >\n @if (\n simpleRichContentNodes(primary(item));\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (primary(item)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"primary(item)\"\n ></praxis-list-metric>\n } @else if (primary(item)?.type === \"html\") {\n <span [innerHTML]=\"primary(item)?.value\"></span>\n } @else {\n {{ primary(item)?.value }}\n }\n </div>\n @if (layoutLines > 1) {\n <div\n class=\"secondary\"\n [ngClass]=\"secondary(item)?.class\"\n [style.cssText]=\"secondary(item)?.style\"\n >\n @if (\n simpleRichContentNodes(secondary(item));\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (\n item.segmentVariant &&\n item.segmentLabel &&\n item.accountType &&\n item.since\n ) {\n <!-- Component-based rendering for executive items -->\n <span class=\"exec-subline\">\n <praxis-executive-badge\n [variant]=\"item.segmentVariant\"\n [label]=\"item.segmentLabel\"\n ></praxis-executive-badge>\n <span class=\"exec-subcopy\">{{\n item.accountType\n }}</span>\n <span class=\"exec-subseparator\">\u00B7</span>\n <span class=\"exec-subcopy\"\n >Desde {{ item.since }}</span\n >\n </span>\n } @else if (secondary(item)?.type === \"html\") {\n <span [innerHTML]=\"secondary(item)?.value\"></span>\n } @else if (secondary(item)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"secondary(item)\"\n ></praxis-list-metric>\n } @else {\n {{ secondary(item)?.value }}\n }\n </div>\n }\n @if (meta(item); as m) {\n @if (\n (config.templating?.metaPlacement === \"line\" &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")) ||\n (layoutLines > 2 &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\"))\n ) {\n <div\n class=\"tertiary\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (config.templating?.metaPrefixIcon; as mpi) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n {{ m.value }}\n </div>\n }\n }\n @if (featuresVisible()) {\n <div class=\"features\">\n @for (\n f of config.templating?.features || [];\n track $index;\n let fi = $index\n ) {\n <span\n class=\"feature\"\n [ngClass]=\"\n featureSemanticClass(item, f.expr, f.class)\n \"\n >\n <ng-container\n *ngTemplateOutlet=\"\n featureContent;\n context: { $implicit: item, feature: f }\n \"\n ></ng-container>\n </span>\n }\n </div>\n }\n </div>\n </button>\n <div class=\"list-item-trailing\">\n @if (meta(item); as m) {\n @if (\n !(\n (config.templating?.metaPlacement === \"line\" ||\n layoutLines > 2) &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")\n )\n ) {\n <div\n class=\"meta\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (\n simpleRichContentNodes(m, { slot: \"meta\" });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(m.color)\n ? m.color\n : undefined\n \"\n [ngClass]=\"\n (m.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(m.color, m.variant)\n \"\n >{{ m.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(m);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, m.value)\"\n [color]=\"ratingThemeColor(m)\"\n [style.cssText]=\"ratingIconStyle(m)\"\n ></mat-icon>\n }\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"m.value\"\n [color]=\"\n isThemeColor(m.color)\n ? m.color\n : undefined\n \"\n [style.cssText]=\"iconStyle(m.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img\n [src]=\"m.value\"\n [alt]=\"m.imageAlt || ''\"\n /></span>\n }\n @case (\"html\") {\n <span [innerHTML]=\"m.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"m\"\n ></praxis-list-metric>\n }\n @default {\n <span>\n @if (\n config.templating?.metaPrefixIcon;\n as mpi2\n ) {\n <mat-icon [praxisIcon]=\"mpi2\"></mat-icon>\n }\n {{ m.value }}</span\n >\n }\n }\n }\n </div>\n }\n }\n @if (trailing(item); as tr) {\n <div\n class=\"trailing\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n @if (\n simpleRichContentNodes(tr, {\n imageAlt:\n config.templating?.trailing?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (tr.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr.color)\n ? tr.color\n : undefined\n \"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(tr.color, tr.variant)\n \"\n >{{ tr.value }}</mat-chip\n >\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"\n isThemeColor(tr.color)\n ? tr.color\n : undefined\n \"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n <img\n [src]=\"tr.value\"\n [alt]=\"\n config.templating?.trailing?.imageAlt ||\n ''\n \"\n />\n @if (tr.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(tr.badge?.color)\n ? tr.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (tr.badge?.variant || 'filled') ===\n 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n tr.badge?.color,\n tr.badge?.variant\n )\n \"\n >{{ tr.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(tr);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, tr.value)\"\n [color]=\"ratingThemeColor(tr)\"\n [style.cssText]=\"ratingIconStyle(tr)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n @if (\n item.alertsData !== undefined &&\n item.ownerName\n ) {\n <!-- Component-based rendering for executive trailing -->\n <span class=\"exec-trailing-shell\">\n <praxis-executive-alerts\n [alerts]=\"item.alertsData\"\n ></praxis-executive-alerts>\n <praxis-executive-owner\n [name]=\"item.ownerName\"\n ></praxis-executive-owner>\n </span>\n } @else {\n <span [innerHTML]=\"tr.value\"></span>\n }\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"tr\"\n ></praxis-list-metric>\n }\n @default {\n <span>{{ tr.value }}</span>\n }\n }\n }\n </div>\n }\n @if (showExpandIcon(\"trailing\")) {\n <button\n type=\"button\"\n [praxisIconButton]=\"\n isExpanded(item, i) ? 'expand_less' : 'expand_more'\n \"\n size=\"compact\"\n class=\"expand-toggle\"\n [attr.aria-label]=\"expandToggleAriaLabel(item, i)\"\n [attr.aria-expanded]=\"\n expansionOwnedByIcon() ? isExpanded(item, i) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByIcon()\n ? expandRegionId(item, i)\n : null\n \"\n (click)=\"onExpandToggle($event, item, i)\"\n ></button>\n }\n @if ((visibleActions(item, \"trailing\") || []).length) {\n <div\n class=\"list-item-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(item, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, item, i)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'stroked')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'raised')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant ||\n action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'flat')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n </div>\n </div>\n }\n @if (isExpandable() && isExpanded(item, i)) {\n <div\n class=\"item-expansion\"\n [ngClass]=\"expansionRenderingClasses()\"\n [style]=\"expansionRenderingStyle()\"\n [id]=\"expandRegionId(item, i)\"\n role=\"region\"\n [attr.aria-label]=\"expansionRegionAriaLabel(item)\"\n >\n <div class=\"item-expansion-grid\">\n @for (\n expansionSection of expansionSections(item);\n track expansionSection.id\n ) {\n <section\n class=\"expansion-section\"\n [attr.data-section-type]=\"expansionSection.type\"\n >\n @if (expansionSection.title) {\n <div class=\"expansion-section-title\">\n {{ expansionSection.title }}\n </div>\n }\n @if (\n expansionSectionHasContent(\n item,\n expansionSection,\n i\n )\n ) {\n @switch (expansionSection.type) {\n @case (\"chip-list\") {\n <div class=\"expansion-chip-list\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <mat-chip class=\"expansion-chip\">{{\n expansionItemLabel(expansionEntry)\n }}</mat-chip>\n }\n </div>\n }\n @case (\"timeline\") {\n <div class=\"expansion-timeline\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-timeline-item\">\n <div class=\"expansion-timeline-title\">\n {{\n expansionTimelineTitle(expansionEntry)\n }}\n </div>\n @if (\n expansionTimelineMeta(expansionEntry)\n ) {\n <div class=\"expansion-timeline-meta\">\n {{\n expansionTimelineMeta(\n expansionEntry\n )\n }}\n </div>\n }\n @if (\n expansionTimelineDescription(\n expansionEntry\n )\n ) {\n <div\n class=\"expansion-timeline-description\"\n >\n {{\n expansionTimelineDescription(\n expansionEntry\n )\n }}\n </div>\n }\n </div>\n }\n </div>\n }\n @case (\"key-value\") {\n <dl class=\"expansion-key-value\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-key-value-row\">\n @if (\n expansionKeyValueKey(expansionEntry)\n ) {\n <dt>\n {{\n expansionKeyValueKey(expansionEntry)\n }}\n </dt>\n }\n <dd>\n {{\n expansionKeyValueValue(expansionEntry)\n }}\n </dd>\n </div>\n }\n </dl>\n }\n @case (\"rich-content\") {\n @if (\n expansionRichContentDocument(\n item,\n expansionSection,\n i\n );\n as richExpansionDocument\n ) {\n <praxis-rich-content\n [document]=\"richExpansionDocument\"\n [context]=\"\n expansionRichContentContext(\n item,\n expansionSection,\n i\n )\n \"\n [hostCapabilities]=\"\n expansionRichContentHostCapabilities(\n item,\n expansionSection,\n i\n )\n \"\n rootClassName=\"praxis-list-expansion-rich-content\"\n ></praxis-rich-content>\n }\n }\n @default {\n <div class=\"expansion-info-list\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-info-item\">\n <div class=\"expansion-info-title\">\n {{ expansionInfoTitle(expansionEntry) }}\n </div>\n @if (expansionInfoValue(expansionEntry)) {\n <div class=\"expansion-info-value\">\n {{\n expansionInfoValue(expansionEntry)\n }}\n </div>\n }\n </div>\n }\n </div>\n }\n }\n } @else {\n <div class=\"expansion-empty\">\n {{\n expansionSection.emptyLabel ||\n \"Sem dados dispon\u00EDveis\"\n }}\n </div>\n }\n </section>\n }\n </div>\n </div>\n }\n </mat-list-item>\n @if (\n (config.layout?.dividers === \"all\" ||\n config.layout?.dividers === \"between\") &&\n i < section.items.length - 1\n ) {\n <mat-divider></mat-divider>\n }\n }\n }\n </mat-list>\n </ng-template>\n } @else if (isTilesVariant()) {\n <ng-container *ngTemplateOutlet=\"tilesVariant\"></ng-container>\n } @else {\n <ng-container *ngTemplateOutlet=\"cardsVariant\"></ng-container>\n }\n\n <!-- Cards variant -->\n <ng-template #cardsVariant>\n <div class=\"cards-grid\">\n @for (it of (items$ | async) ?? []; track $index; let i = $index) {\n <div\n class=\"item-card\"\n [ngClass]=\"itemRuleClass(it)\"\n [attr.style]=\"itemRuleStyle(it)\"\n >\n <div\n class=\"list-item-content card-main-action\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"itemAriaLabel(it)\"\n (click)=\"onItemClick(it, i)\"\n (keydown.enter)=\"onItemClick(it, i)\"\n (keydown.space)=\"$event.preventDefault(); onItemClick(it, i)\"\n >\n <div class=\"list-item-leading\">\n @if (leading(it); as lead) {\n @if (\n simpleRichContentNodes(lead, {\n imageAlt: config.templating?.leading?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"lead.value\"\n [ngClass]=\"lead.class\"\n [color]=\"\n isThemeColor(lead.color) ? lead.color : undefined\n \"\n [style.cssText]=\"\n (lead.style ? lead.style + ';' : '') +\n iconStyle(lead.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >\n <img\n [src]=\"lead.value\"\n [alt]=\"config.templating?.leading?.imageAlt || ''\"\n />\n @if (lead.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(lead.badge?.color)\n ? lead.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (lead.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n lead.badge?.color,\n lead.badge?.variant\n )\n \"\n >{{ lead.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"text\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(lead.color) ? lead.color : undefined\n \"\n [ngClass]=\"\n (lead.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(lead.color, lead.variant)\"\n >{{ lead.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(lead);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, lead.value)\"\n [color]=\"ratingThemeColor(lead)\"\n [style.cssText]=\"ratingIconStyle(lead)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n [innerHTML]=\"lead.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"lead\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"lead\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"lead\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n }\n }\n } @else {\n <span class=\"leading-placeholder\"></span>\n }\n </div>\n <div class=\"list-item-text\">\n <div\n class=\"primary\"\n [ngClass]=\"primary(it)?.class\"\n [style.cssText]=\"primary(it)?.style\"\n >\n @if (simpleRichContentNodes(primary(it)); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (primary(it)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"primary(it)\"\n ></praxis-list-metric>\n } @else if (primary(it)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"primary(it)\"\n ></praxis-list-compose>\n } @else if (primary(it)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"primary(it)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ primary(it)?.value }}\n }\n </div>\n @if (layoutLines > 1) {\n <div\n class=\"secondary\"\n [ngClass]=\"secondary(it)?.class\"\n [style.cssText]=\"secondary(it)?.style\"\n >\n @if (simpleRichContentNodes(secondary(it)); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (secondary(it)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"secondary(it)\"\n ></praxis-list-metric>\n } @else if (secondary(it)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"secondary(it)\"\n ></praxis-list-compose>\n } @else if (secondary(it)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"secondary(it)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ secondary(it)?.value }}\n }\n </div>\n }\n @if (meta(it); as m) {\n @if (\n (config.templating?.metaPlacement === \"line\" &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")) ||\n (layoutLines > 2 &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\"))\n ) {\n <div\n class=\"tertiary\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (config.templating?.metaPrefixIcon; as mpi) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n {{ m.value }}\n </div>\n }\n }\n @if (featuresVisible()) {\n <div class=\"features\">\n @for (\n f of config.templating?.features || [];\n track $index;\n let fi = $index\n ) {\n <span\n class=\"feature\"\n [ngClass]=\"featureSemanticClass(it, f.expr, f.class)\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n featureContent;\n context: { $implicit: it, feature: f }\n \"\n ></ng-container>\n </span>\n }\n </div>\n }\n @if (cardDetailSlots(it); as detailSlots) {\n @if (detailSlots.length) {\n <div class=\"card-detail-strip\">\n @for (detail of detailSlots; track detail.slot) {\n <div\n class=\"card-detail\"\n [ngClass]=\"'card-detail--' + detail.slot\"\n >\n <span class=\"card-detail__label\">{{\n detail.label\n }}</span>\n <span class=\"card-detail__value\">\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: detail.node,\n slot: detail.slot,\n imageAlt: rowLayoutImageAlt(detail.slot),\n }\n \"\n ></ng-container>\n </span>\n </div>\n }\n </div>\n }\n }\n </div>\n <div class=\"list-item-trailing\">\n @if (meta(it); as m) {\n @if (\n !(\n (config.templating?.metaPlacement === \"line\" ||\n layoutLines > 2) &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")\n )\n ) {\n <div\n class=\"meta\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (\n simpleRichContentNodes(m, {\n slot: \"meta\",\n imageAlt: m.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(m.color) ? m.color : undefined\n \"\n [ngClass]=\"\n (m.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(m.color, m.variant)\"\n >{{ m.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(m);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, m.value)\"\n [color]=\"ratingThemeColor(m)\"\n [style.cssText]=\"ratingIconStyle(m)\"\n ></mat-icon>\n }\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"m.value\"\n [color]=\"\n isThemeColor(m.color) ? m.color : undefined\n \"\n [style.cssText]=\"iconStyle(m.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img [src]=\"m.value\" [alt]=\"m.imageAlt || ''\"\n /></span>\n }\n @case (\"html\") {\n <span [innerHTML]=\"m.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"m\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"m\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"m\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>\n @if (config.templating?.metaPrefixIcon; as mpi2) {\n <mat-icon [praxisIcon]=\"mpi2\"></mat-icon>\n }\n {{ m.value }}</span\n >\n }\n }\n }\n </div>\n }\n }\n @if (trailing(it); as tr) {\n @if (\n (config.templating?.statusPosition || \"inline\") ===\n \"top-right\" &&\n (tr?.type === \"chip\" || tr?.type === \"icon\")\n ) {\n <div class=\"status-overlay\">\n @if (simpleRichContentNodes(tr); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @if (tr?.type === \"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(tr.color, tr.variant)\"\n >{{ tr.value }}</mat-chip\n >\n }\n @if (tr?.type === \"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n }\n </div>\n } @else {\n <div\n class=\"trailing\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n @if (\n simpleRichContentNodes(tr, {\n imageAlt: config.templating?.trailing?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (tr.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(tr.color, tr.variant)\"\n >{{ tr.value }}</mat-chip\n >\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n <img\n [src]=\"tr.value\"\n [alt]=\"\n config.templating?.trailing?.imageAlt || ''\n \"\n />\n @if (tr.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(tr.badge?.color)\n ? tr.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (tr.badge?.variant || 'filled') ===\n 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n tr.badge?.color,\n tr.badge?.variant\n )\n \"\n >{{ tr.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(tr);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, tr.value)\"\n [color]=\"ratingThemeColor(tr)\"\n [style.cssText]=\"ratingIconStyle(tr)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n <span [innerHTML]=\"tr.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"tr\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"tr\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"tr\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>{{ tr.value }}</span>\n }\n }\n }\n </div>\n }\n }\n </div>\n </div>\n <div\n class=\"card-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(it, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, it, i)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'stroked')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'raised')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant || action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'flat')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n </div>\n }\n </div>\n </ng-template>\n\n <!-- Tiles variant -->\n <ng-template #tilesVariant>\n <div class=\"tiles-grid\">\n @for (it of (items$ | async) ?? []; track $index; let i = $index) {\n <div\n class=\"item-tile\"\n [ngClass]=\"itemRuleClass(it)\"\n [attr.style]=\"itemRuleStyle(it)\"\n >\n <div\n class=\"tile-main-action\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"itemAriaLabel(it)\"\n (click)=\"onItemClick(it, i)\"\n (keydown.enter)=\"onItemClick(it, i)\"\n (keydown.space)=\"$event.preventDefault(); onItemClick(it, i)\"\n >\n <div class=\"tile-media\">\n @if (leading(it); as lead) {\n @if (\n simpleRichContentNodes(lead, {\n imageAlt: config.templating?.leading?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case (\"image\") {\n <img\n [src]=\"lead.value\"\n [alt]=\"config.templating?.leading?.imageAlt || ''\"\n />\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"lead.value\"\n [ngClass]=\"lead.class\"\n [color]=\"\n isThemeColor(lead.color) ? lead.color : undefined\n \"\n [style.cssText]=\"\n (lead.style ? lead.style + ';' : '') +\n iconStyle(lead.color)\n \"\n ></mat-icon>\n }\n @case (\"text\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(lead.color) ? lead.color : undefined\n \"\n [ngClass]=\"\n (lead.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(lead.color, lead.variant)\"\n >{{ lead.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">\n @for (\n _ of ratingRange(lead);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, lead.value)\"\n [color]=\"ratingThemeColor(lead)\"\n [style.cssText]=\"ratingIconStyle(lead)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n [innerHTML]=\"lead.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"lead\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"lead\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"lead\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n }\n }\n }\n </div>\n\n @if (trailing(it); as tr) {\n @if (\n (config.templating?.statusPosition || \"inline\") ===\n \"top-right\" &&\n (tr?.type === \"chip\" || tr?.type === \"icon\")\n ) {\n <div class=\"tile-status\">\n @if (simpleRichContentNodes(tr); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @if (tr?.type === \"chip\") {\n <mat-chip\n [color]=\"isThemeColor(tr.color) ? tr.color : undefined\"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(tr.color, tr.variant)\"\n >{{ tr.value }}</mat-chip\n >\n }\n @if (tr?.type === \"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"isThemeColor(tr.color) ? tr.color : undefined\"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n }\n </div>\n }\n }\n\n <div class=\"tile-body\">\n <div\n class=\"primary\"\n [ngClass]=\"primary(it)?.class\"\n [style.cssText]=\"primary(it)?.style\"\n >\n @if (simpleRichContentNodes(primary(it)); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (primary(it)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"primary(it)\"\n ></praxis-list-metric>\n } @else if (primary(it)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"primary(it)\"\n ></praxis-list-compose>\n } @else if (primary(it)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"primary(it)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ primary(it)?.value }}\n }\n </div>\n @if (layoutLines > 1) {\n <div\n class=\"secondary\"\n [ngClass]=\"secondary(it)?.class\"\n [style.cssText]=\"secondary(it)?.style\"\n >\n @if (simpleRichContentNodes(secondary(it)); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (secondary(it)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"secondary(it)\"\n ></praxis-list-metric>\n } @else if (secondary(it)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"secondary(it)\"\n ></praxis-list-compose>\n } @else if (secondary(it)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"secondary(it)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ secondary(it)?.value }}\n }\n </div>\n }\n\n @if (meta(it); as m) {\n <div\n class=\"tile-meta\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (config.templating?.metaPrefixIcon; as mpi) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n @if (\n simpleRichContentNodes(m, {\n slot: \"meta\",\n imageAlt: m.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"isThemeColor(m.color) ? m.color : undefined\"\n [ngClass]=\"\n (m.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(m.color, m.variant)\"\n >{{ m.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(m);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, m.value)\"\n [color]=\"ratingThemeColor(m)\"\n [style.cssText]=\"ratingIconStyle(m)\"\n ></mat-icon>\n }\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"m.value\"\n [color]=\"isThemeColor(m.color) ? m.color : undefined\"\n [style.cssText]=\"iconStyle(m.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img [src]=\"m.value\" [alt]=\"m.imageAlt || ''\"\n /></span>\n }\n @case (\"html\") {\n <span [innerHTML]=\"m.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"m\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"m\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"m\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>{{ m.value }}</span>\n }\n }\n }\n </div>\n }\n\n @if (featuresVisible()) {\n <div class=\"tiles-features\">\n @for (\n f of config.templating?.features || [];\n track $index;\n let fi = $index\n ) {\n <span\n class=\"feature\"\n [ngClass]=\"featureSemanticClass(it, f.expr, f.class)\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n featureContent;\n context: { $implicit: it, feature: f }\n \"\n ></ng-container>\n </span>\n }\n </div>\n }\n\n @if (trailing(it); as tr2) {\n @if (\n !(\n (config.templating?.statusPosition || \"inline\") ===\n \"top-right\" &&\n (tr2?.type === \"chip\" || tr2?.type === \"icon\")\n )\n ) {\n <div\n class=\"tile-trailing\"\n [ngClass]=\"tr2.class\"\n [style.cssText]=\"tr2.style\"\n >\n @if (\n simpleRichContentNodes(tr2, {\n imageAlt: tr2.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (tr2.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr2.color) ? tr2.color : undefined\n \"\n [ngClass]=\"\n (tr2.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(tr2.color, tr2.variant)\"\n >{{ tr2.value }}</mat-chip\n >\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"tr2.value\"\n [color]=\"\n isThemeColor(tr2.color) ? tr2.color : undefined\n \"\n [style.cssText]=\"iconStyle(tr2.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img [src]=\"tr2.value\" [alt]=\"tr2.imageAlt || ''\"\n /></span>\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(tr2);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, tr2.value)\"\n [color]=\"ratingThemeColor(tr2)\"\n [style.cssText]=\"ratingIconStyle(tr2)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n <span [innerHTML]=\"tr2.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"tr2\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"tr2\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"tr2\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>{{ tr2.value }}</span>\n }\n }\n }\n </div>\n }\n }\n </div>\n </div>\n\n @if ((visibleActions(it, \"trailing\") || []).length) {\n <div\n class=\"tile-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(it, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, it, i)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'stroked')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'raised')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant || action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'flat')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n </div>\n }\n </div>\n </ng-template>\n\n <!-- Remote pagination stays attached to the collection footer. -->\n @if (isRemoteDataMode() && !(error$ | async)) {\n @if (page$ | async; as ps) {\n @if (total$ | async; as total) {\n @if (config.ui?.showRange === false) {\n <div class=\"list-paginator-total\" aria-live=\"polite\">\n {{ remoteTotalLabel(total) }}\n </div>\n }\n <mat-paginator\n [length]=\"total || 0\"\n [pageIndex]=\"ps.pageNumber || 0\"\n [pageSize]=\"ps.pageSize || config.layout?.pageSize || 10\"\n [pageSizeOptions]=\"listPageSizeOptions()\"\n [selectConfig]=\"paginatorSelectConfig\"\n [showFirstLastButtons]=\"true\"\n [class.hide-range]=\"config.ui?.showRange === false\"\n (page)=\"onPageChange($event)\"\n >\n </mat-paginator>\n }\n }\n }\n </ng-template>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block}.praxis-list-root{container-type:inline-size;--p-list-radius: var(--md-sys-shape-corner-medium, 16px);--p-list-shadow: var(--md-sys-elevation-level2);--p-list-border: 1px solid var(--md-sys-color-outline-variant);--p-list-blur: 10px;--p-list-surface: var(--md-sys-color-surface-container);--p-list-surface-low: var(--md-sys-color-surface);--p-list-surface-high: var( --md-sys-color-surface-container-high, var(--md-sys-color-surface-container) );--p-list-foreground: var(--md-sys-color-on-surface);--p-list-foreground-muted: var(--md-sys-color-on-surface-variant);--p-list-accent: var(--md-sys-color-primary);--p-list-accent-weak: var(--md-sys-color-primary-container);--p-list-item-surface: var( --md-sys-color-surface-container-low, var(--md-sys-color-surface) );--p-list-item-border: 1px solid var(--md-sys-color-outline-variant);--p-list-item-hover-surface: var(--md-sys-color-surface-container-low);--p-list-item-active-surface: var(--md-sys-color-surface-container);--p-list-item-selected-surface: var(--md-sys-color-surface-container);--p-list-grad-from: var(--md-sys-color-primary-container);--p-list-grad-to: var(--md-sys-color-tertiary-container);--p-list-grad-angle: 135deg;--p-list-grad-foreground: var( --md-sys-color-on-primary-container, var(--md-sys-color-on-surface) );--p-list-grad-foreground-muted: color-mix( in srgb, var(--p-list-grad-foreground) 76%, transparent );--p-list-leading-width: 36px;--p-list-trailing-min-width: 140px;--p-list-trailing-max-width: 320px;--p-list-selection-end-gutter: 56px;--p-list-item-gap: 10px;--p-list-item-padding-x: 14px;--p-list-item-padding-y: 10px;--p-list-item-stack-gap: 0px;--p-list-meta-size: .875rem;--p-list-meta-weight: 500;--p-list-chip-height: 22px;--p-list-chip-font-size: 12px;--p-list-trailing-padding-right: 8px;--p-list-action-button-height: 32px;--p-list-tile-minW: 240px;--p-list-tile-gap: 12px;--p-list-tile-padding: 12px;--p-list-tile-radius: 12px;--p-list-tile-media-radius: 12px;--p-list-tile-media-ratio: 1 / 1;--p-list-tile-hover-overlay: .04;--p-list-tile-press-overlay: .08;--p-list-tile-press-scale: .99}.list-assistant{display:flex;justify-content:flex-end;gap:8px;padding:4px 4px 8px}.action-loading{opacity:.65}.action-spinner{width:16px;height:16px;display:inline-flex;align-items:center;justify-content:center}.action-loading .mat-mdc-button-touch-target,.action-loading .mdc-button__label{opacity:.7}.skin-elevated,.skin-outline,.skin-flat,.skin-neumorphism{--p-list-item-surface: var( --mdc-elevated-card-container-color, var(--p-list-surface) );--p-list-item-border: var(--p-list-border)}.skin-elevated .item-card,.skin-outline .item-card,.skin-flat .item-card,.skin-neumorphism .item-card{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:var(--p-list-radius);box-shadow:var(--p-list-shadow);border:var(--p-list-border)}.skin-elevated .item-tile,.skin-outline .item-tile,.skin-flat .item-tile,.skin-neumorphism .item-tile{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));box-shadow:var(--p-list-shadow);border:var(--p-list-border)}.skin-elevated .mat-mdc-list-item .list-item-content,.skin-outline .mat-mdc-list-item .list-item-content,.skin-flat .mat-mdc-list-item .list-item-content,.skin-neumorphism .mat-mdc-list-item .list-item-content{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * .75);box-shadow:var(--p-list-shadow);border:var(--p-list-item-border);color:var(--p-list-foreground)}.skin-outline{--p-list-shadow: none;--p-list-border: 1px solid var(--md-sys-color-outline)}.skin-flat{--p-list-shadow: none;--p-list-border: 1px solid var(--md-sys-color-outline-variant)}.skin-neumorphism{--p-list-shadow: var(--md-sys-elevation-level2);--p-list-border: 1px solid var(--md-sys-color-outline-variant);--p-list-radius: 1.25rem}.skin-pill-soft .item-card,.skin-pill-soft .item-tile{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:var(--md-sys-elevation-level1);border:var(--p-list-border)}.skin-pill-soft .mat-mdc-list-item .list-item-content{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:var(--md-sys-elevation-level1);border:var(--p-list-border);color:var(--p-list-foreground)}.skin-gradient-tile{--p-list-foreground: var(--p-list-grad-foreground);--p-list-foreground-muted: var(--p-list-grad-foreground-muted);--p-list-item-hover-surface: linear-gradient( var(--p-list-grad-angle), var(--p-list-grad-from), var(--p-list-grad-to) );--p-list-item-active-surface: linear-gradient( var(--p-list-grad-angle), var(--p-list-grad-from), var(--p-list-grad-to) );--p-list-item-selected-surface: linear-gradient( var(--p-list-grad-angle), var(--p-list-grad-from), var(--p-list-grad-to) )}.skin-gradient-tile .item-card,.skin-gradient-tile .item-tile{background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));border-radius:var(--p-list-radius);color:var(--p-list-foreground)}.skin-gradient-tile .tile-media{background:color-mix(in srgb,var(--md-sys-color-surface) 74%,transparent);border-color:color-mix(in srgb,var(--p-list-foreground) 22%,transparent)}.skin-gradient-tile .tile-media mat-icon,.skin-gradient-tile .tile-meta,.skin-gradient-tile .tile-trailing,.skin-gradient-tile .feature,.skin-gradient-tile .secondary,.skin-gradient-tile .meta{color:var(--p-list-foreground-muted)}.skin-gradient-tile .tile-status,.skin-gradient-tile [data-rich-node-type=badge] .prx-rich-badge{color:var(--md-sys-color-on-secondary-container, var(--p-list-foreground));background:var(--md-sys-color-secondary-container, rgba(255, 255, 255, .72))}.skin-gradient-tile .mat-mdc-list-item .list-item-content{background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));border-radius:var(--p-list-radius);color:var(--p-list-foreground)}.skin-glass .item-card,.skin-glass .item-tile{background:var(--md-sys-color-surface-container-low);border-radius:var(--p-list-radius);border:1px solid var(--md-sys-color-outline-variant);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur))}.skin-glass .mat-mdc-list-item .list-item-content{background:var(--md-sys-color-surface-container-low);border-radius:var(--p-list-radius);border:1px solid var(--md-sys-color-outline-variant);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur));color:var(--p-list-foreground)}.density-compact .mat-mdc-list-item,.density-compact .item-card{--p-list-item-padding-y: 6px;--p-list-item-padding-x: 10px;--p-list-item-gap: 8px}.density-comfortable .mat-mdc-list-item,.density-comfortable .item-card{--p-list-item-padding-y: 8px;--p-list-item-padding-x: 12px;--p-list-item-gap: 10px}.praxis-list-root.lines-3 .mat-mdc-list-item,.praxis-list-root.lines-3 .mat-mdc-list-option{min-height:68px}.density-compact{--p-list-tile-gap: 10px;--p-list-tile-padding: 10px;--p-list-tile-minW: 188px}.density-comfortable{--p-list-tile-gap: 12px;--p-list-tile-padding: 12px}.item-spacing-none{--p-list-item-stack-gap: 0px}.item-spacing-tight{--p-list-item-stack-gap: 4px;--p-list-tile-gap: 10px}.item-spacing-default{--p-list-item-stack-gap: 6px;--p-list-tile-gap: 12px}.item-spacing-relaxed{--p-list-item-stack-gap: 14px;--p-list-tile-gap: 20px}.list-item-content{box-sizing:border-box;display:grid;grid-template-columns:var(--p-list-leading-width) minmax(0,1fr) minmax(var(--p-list-trailing-min-width),min(34vw,var(--p-list-trailing-max-width)));align-items:center;gap:var(--p-list-item-gap);padding:var(--p-list-item-padding-y) var(--p-list-item-padding-x);width:100%;min-width:0;border-radius:calc(var(--p-list-radius) * .75);overflow:visible}.list-item-content--row-layout{--p-list-row-template-columns: var(--p-list-leading-width) minmax(0, 1fr) minmax(var(--p-list-trailing-min-width), max-content);--p-list-row-gap: var(--p-list-item-gap);--p-list-row-align-items: center;grid-template-columns:var(--p-list-row-template-columns);gap:var(--p-list-row-gap);align-items:var(--p-list-row-align-items)}.list-item-content--row-layout[role=button]{cursor:pointer}.list-item-content--row-layout[role=button]:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.list-row-slot{min-width:0;height:100%;align-self:stretch;display:flex;align-items:var(--p-list-row-slot-align-items, center);gap:8px}.list-item-content--row-layout .list-row-slot>praxis-rich-content,.list-item-content--row-layout .list-row-slot>praxis-list-metric,.list-item-content--row-layout .list-row-slot>praxis-list-compose,.list-item-content--row-layout .list-row-slot>praxis-list-runtime-component{min-width:0}.list-row-slot--leading{justify-content:center}.list-row-slot--primary,.list-row-slot--secondary{min-width:0}.list-row-slot--meta{color:var(--p-list-foreground);font-variant-numeric:tabular-nums}.list-row-slot--trailing{flex-wrap:wrap;justify-content:flex-end}.list-row-slot .primary,.list-row-slot .secondary,.list-row-slot .meta,.list-row-slot .trailing{min-width:0}.list-item-main-action{grid-column:1/3;display:grid;grid-template-columns:var(--p-list-leading-width) minmax(0,1fr);align-items:center;gap:var(--p-list-item-gap);min-width:0;padding:0;border:0;background:transparent;color:inherit;text-align:start;font:inherit;cursor:pointer}.list-item-main-action:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px;border-radius:12px}.list-item-leading{display:flex;align-items:center;justify-content:center;min-height:100%}.leading-placeholder{width:24px;height:24px;display:inline-block}.list-item-text{min-width:0;display:grid;align-content:center;gap:2px}.list-item-trailing{box-sizing:border-box;min-width:0;width:100%;max-width:var(--p-list-trailing-max-width);display:grid;align-self:center;align-items:center;justify-content:end;justify-items:end;align-content:center;gap:4px;text-align:end;padding-right:var(--p-list-trailing-padding-right)}.list-item-trailing>*{align-self:center}.list-item-content--row-layout .list-item-actions{justify-content:flex-end}.list-item-actions{width:max-content;justify-self:end;align-self:center;display:flex;align-items:center;justify-content:flex-end;gap:4px;flex-wrap:wrap;max-width:100%;min-height:var(--p-list-action-button-height)}.list-item-actions .mat-mdc-button-base{min-height:var(--p-list-action-button-height);height:var(--p-list-action-button-height);display:inline-flex;align-items:center}.expand-toggle{color:var(--p-list-foreground-muted)}.mat-mdc-list-item .list-item-content{background:var(--p-list-item-surface);border:var(--p-list-item-border);transition:background .15s ease,box-shadow .15s ease,transform .15s ease}.mat-mdc-list-item,.mat-mdc-list-option{height:auto;align-items:stretch;overflow:visible}.mat-mdc-list-option .mdc-list-item__end{align-self:flex-start;margin-top:var(--p-list-item-padding-y);margin-bottom:0}.mat-mdc-list-option .list-item-content{padding-right:calc(var(--p-list-item-padding-x) + var(--p-list-selection-end-gutter))}.mat-mdc-list-item.mdc-list-item--with-one-line,.mat-mdc-list-item.mdc-list-item--with-two-lines,.mat-mdc-list-item.mdc-list-item--with-three-lines,.mat-mdc-list-item-single-line,.mat-mdc-list-item-two-line,.mat-mdc-list-item-three-line,.mat-mdc-list-option.mdc-list-item--with-one-line,.mat-mdc-list-option.mdc-list-item--with-two-lines,.mat-mdc-list-option.mdc-list-item--with-three-lines{height:auto!important}.mdc-list-item{overflow:visible}.density-compact .mat-mdc-list-item,.density-compact .mat-mdc-list-option{min-height:40px}.density-comfortable .mat-mdc-list-item,.density-comfortable .mat-mdc-list-option{min-height:46px}.praxis-list-root:not(.density-compact):not(.density-comfortable) .mat-mdc-list-item,.praxis-list-root:not(.density-compact):not(.density-comfortable) .mat-mdc-list-option{min-height:48px}.mat-mdc-list-item .mdc-list-item__content,.mat-mdc-list-option .mdc-list-item__content,.mat-mdc-list-item .mdc-list-item__primary-text,.mat-mdc-list-option .mdc-list-item__primary-text{display:block!important;width:100%!important;height:auto!important;padding:0!important;margin:0!important;overflow:visible!important;box-sizing:border-box}.mat-mdc-list-item,.mat-mdc-list-option{padding:0!important;box-sizing:border-box}.praxis-list-root mat-list>mat-list-item:not(:last-child),.praxis-list-root mat-selection-list>mat-list-option:not(:last-child){margin-bottom:var(--p-list-item-stack-gap)}.mat-mdc-list-item:hover .list-item-content,.mat-mdc-list-option:hover .list-item-content{background:var(--p-list-item-hover-surface)}.mat-mdc-list-item:active .list-item-content,.mat-mdc-list-option:active .list-item-content{background:var(--p-list-item-active-surface)}.mat-mdc-list-option.mdc-list-item--selected .list-item-content{background:var(--p-list-item-selected-surface);border-color:var(--md-sys-color-outline-variant)}.mat-mdc-list-option.cdk-keyboard-focused .list-item-content,.mat-mdc-list-option.cdk-focused .list-item-content{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.mat-mdc-list-item:focus-visible .list-item-content{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.praxis-list-root mat-selection-list>.mat-mdc-list-option{border-radius:calc(var(--p-list-radius) * .75);background:var(--p-list-item-surface);border:var(--p-list-item-border);color:var(--p-list-foreground);overflow:hidden;transition:background .15s ease,box-shadow .15s ease,border-color .15s ease}.skin-elevated mat-selection-list>.mat-mdc-list-option,.skin-outline mat-selection-list>.mat-mdc-list-option,.skin-flat mat-selection-list>.mat-mdc-list-option,.skin-neumorphism mat-selection-list>.mat-mdc-list-option{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border:var(--p-list-item-border);box-shadow:var(--p-list-shadow)}.skin-pill-soft mat-selection-list>.mat-mdc-list-option{border-radius:calc(var(--p-list-radius) * 1.3);background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border:var(--p-list-border);box-shadow:var(--md-sys-elevation-level1)}.skin-gradient-tile mat-selection-list>.mat-mdc-list-option{border-radius:var(--p-list-radius);background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));color:var(--p-list-foreground)}.skin-glass mat-selection-list>.mat-mdc-list-option{border-radius:var(--p-list-radius);background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur))}.praxis-list-root mat-selection-list>.mat-mdc-list-option:hover{background:var(--p-list-item-hover-surface)}.praxis-list-root mat-selection-list>.mat-mdc-list-option:active{background:var(--p-list-item-active-surface)}.praxis-list-root mat-selection-list>.mat-mdc-list-option.mdc-list-item--selected{background:var(--p-list-item-selected-surface);border-color:var(--md-sys-color-outline-variant)}.praxis-list-root mat-selection-list>.mat-mdc-list-option .list-item-content{background:transparent;border:0;box-shadow:none;border-radius:0}.praxis-list-root mat-selection-list>.mat-mdc-list-option.cdk-keyboard-focused,.praxis-list-root mat-selection-list>.mat-mdc-list-option.cdk-focused{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.executive-inline-skin{--p-list-leading-width: 56px;--p-list-trailing-min-width: 232px;--p-list-item-padding-x: 14px;--p-list-item-padding-y: 8px;--p-list-item-gap: 10px;--p-list-surface: #f8fafb;--p-list-surface-low: #ffffff;--p-list-item-surface: #ffffff;--p-list-item-hover-surface: #f7fafc;--p-list-item-active-surface: #eef4f8;--p-list-item-selected-surface: #eef4f8;--p-list-foreground: #17324d;--p-list-foreground-muted: #72849a}.executive-inline-skin .list-item-content--row-layout.exec-row-layout{min-height:72px;padding:12px 20px;background:linear-gradient(180deg,#fff,#fbfcfd)!important;border:1px solid #dce5ec!important;border-radius:18px;box-shadow:0 1px 2px #0f223a08,0 4px 14px #0f223a08!important;color:#17324d!important}.executive-inline-skin .mat-mdc-list-item:hover .list-item-content--row-layout.exec-row-layout{background:linear-gradient(180deg,#fff,#f7fafc)!important}.executive-inline-skin .list-item-content--row-layout.exec-row-layout+.item-expansion{margin-left:10px;margin-right:10px}.executive-inline-skin .list-item-content--row-layout.exec-row-layout .list-row-slot{min-height:34px}.executive-inline-skin .list-row-slot--leading{justify-content:center}.executive-inline-skin .list-row-slot--identity{align-items:center;padding-right:4px}.executive-inline-skin .list-row-slot--balance,.executive-inline-skin .list-row-slot--limit,.executive-inline-skin .list-row-slot--risk,.executive-inline-skin .list-row-slot--alerts{justify-content:center;padding-inline:2px}.executive-inline-skin .list-row-slot--owner{justify-content:flex-start;padding-left:0}.executive-inline-skin .list-row-slot--actions{justify-content:flex-end;padding-left:0}.executive-inline-skin .list-row-slot--expand{justify-content:center}.executive-inline-skin .docs-expansion-leading{width:44px;height:44px;display:inline-flex;align-items:center;justify-content:center;border-radius:15px;background:linear-gradient(180deg,#edf4f9,#e6eef5);color:#1b5f86;font-size:1rem;font-weight:800;letter-spacing:.02em;box-shadow:0 1px 3px #1b5f861f;border:1px solid #dbe7f0}.executive-inline-skin .exec-balance-metric,.executive-inline-skin .exec-limit-metric,.executive-inline-skin .exec-risk-metric{min-width:0}.executive-inline-skin .exec-balance-metric .p-list-metric,.executive-inline-skin .exec-limit-metric .p-list-metric,.executive-inline-skin .exec-risk-metric .p-list-metric{gap:5px}.executive-inline-skin .exec-balance-metric .p-list-metric__label,.executive-inline-skin .exec-limit-metric .p-list-metric__label,.executive-inline-skin .exec-risk-metric .p-list-metric__label{font-size:.62rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#8090a1}.executive-inline-skin .exec-balance-metric .p-list-metric__value,.executive-inline-skin .exec-limit-metric .p-list-metric__value{font-size:.96rem;font-weight:800;letter-spacing:-.02em;color:#0d1f34;line-height:1.05}.executive-inline-skin .exec-limit-metric .p-list-metric__caption{font-size:.66rem;font-weight:700;color:#8090a1}.executive-inline-skin .exec-limit-metric .p-list-metric__progress{width:68px;justify-self:center;height:3px}.executive-inline-skin .exec-risk-metric{min-width:84px}.executive-inline-skin .exec-risk-metric .p-list-metric{justify-items:center}.executive-inline-skin .exec-risk-metric .p-list-metric__main{gap:6px}.executive-inline-skin .exec-risk-metric .p-list-metric__value{font-size:1.02rem;font-weight:800;letter-spacing:-.024em;line-height:1}.executive-inline-skin .exec-risk-metric .p-list-metric__progress{width:56px;justify-self:center;height:3px}.executive-inline-skin .exec-risk-metric .p-list-metric__caption{font-size:.7rem;font-weight:700;line-height:1.1}.executive-inline-skin .exec-risk-metric .p-list-metric__caption--below-progress{justify-self:center;margin-top:-1px}.executive-inline-skin .exec-risk-metric .p-list-metric__icon mat-icon{width:15px;height:15px;font-size:15px}.executive-inline-skin .list-item-actions{gap:6px;flex-wrap:nowrap}.executive-inline-skin .list-row-slot--actions .list-item-actions{justify-content:flex-end}.executive-inline-skin .list-item-actions button,.executive-inline-skin .expand-toggle{width:30px;height:30px;min-width:30px;border-radius:12px;color:#6d7f93!important}.executive-inline-skin .list-item-actions button mat-icon,.executive-inline-skin .expand-toggle mat-icon{width:18px;height:18px;font-size:18px}.executive-inline-skin .item-expansion{position:relative;margin-top:-10px;padding:22px 26px;background:linear-gradient(180deg,#fcfdfe,#f5f8fb);border-color:#e4eaf0;border-radius:0 0 22px 22px;box-shadow:inset 0 1px #ffffffdb}.lead-image{position:relative;width:96px;height:72px;border-radius:12px;overflow:hidden}.lead-image img{width:100%;height:100%;object-fit:cover;display:block}.lead-image .lead-badge{position:absolute;left:8px;bottom:8px}.inline-image{position:relative;width:20px;height:20px;border-radius:4px;overflow:hidden;display:inline-block;vertical-align:middle}.inline-image img{width:100%;height:100%;object-fit:cover;display:block}.inline-image .inline-badge{position:absolute;left:2px;bottom:2px}.model-media .lead-image{width:136px;height:96px;border-radius:16px}.model-media .list-item-content{gap:16px}.model-media .item-card{--p-list-leading-width: 136px}.model-hotel .lead-image{width:160px;height:110px;border-radius:18px}.model-hotel .item-card{--p-list-leading-width: 160px}.primary,.secondary{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis}.primary{-webkit-line-clamp:1;font-weight:600;color:var(--p-list-foreground)}.secondary{-webkit-line-clamp:1;color:var(--p-list-foreground-muted)}.lines-3 .tertiary{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.item-tile .primary{-webkit-line-clamp:2;font-size:.95rem;line-height:1.25rem}.item-tile .secondary{-webkit-line-clamp:2;font-size:.8rem;line-height:1.1rem}.tertiary{color:var(--p-list-foreground-muted)}.features{display:flex;flex-wrap:wrap;gap:12px;margin-top:6px;color:inherit}.feature{display:inline-flex;align-items:center;gap:6px}.feature-line{display:block}.feature-progress{display:block;width:88px;height:4px;margin-top:6px;border-radius:999px;background:#dbe5eb;overflow:hidden}.feature-progress__value{display:block;height:100%;border-radius:inherit;background:#2f9b69}.meta{color:var(--p-list-foreground);font-size:var(--p-list-meta-size);font-weight:var(--p-list-meta-weight);font-variant-numeric:tabular-nums;max-width:100%;min-width:0;display:inline-flex;align-items:center;justify-content:flex-end;gap:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.meta>span,.meta praxis-rich-content{min-width:0;display:inline-flex;align-items:center;justify-content:flex-end;gap:4px;line-height:1.15;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.meta .mat-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;line-height:1;vertical-align:middle}.trailing{color:var(--p-list-foreground-muted);margin-left:0;max-width:100%;min-width:0;display:inline-flex;justify-content:flex-end;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.trailing praxis-rich-content{min-width:0}:host ::ng-deep .meta praxis-rich-content .prx-rich-node[data-rich-node-type=badge],:host ::ng-deep .trailing praxis-rich-content .prx-rich-node[data-rich-node-type=badge],:host ::ng-deep .list-row-slot praxis-rich-content .prx-rich-node[data-rich-node-type=badge]{background:transparent;color:inherit}:host ::ng-deep .meta praxis-rich-content .prx-rich-node[data-rich-node-type=badge] .prx-rich-badge,:host ::ng-deep .trailing praxis-rich-content .prx-rich-node[data-rich-node-type=badge] .prx-rich-badge,:host ::ng-deep .list-row-slot praxis-rich-content .prx-rich-node[data-rich-node-type=badge] .prx-rich-badge{min-height:22px;padding:1px 9px;border:1px solid var(--p-list-chip-border-color, transparent);background:var(--p-list-chip-background, var(--md-sys-color-primary-container, #e8def8));color:var(--p-list-chip-color, var(--md-sys-color-on-primary-container, #21005d));line-height:18px}.section-header{font-size:.85rem;color:var(--p-list-foreground-muted);padding:8px 12px}.section-header--setup{margin:8px 12px 12px;padding:14px 16px;border:1px dashed var(--md-sys-color-outline-variant);border-radius:8px;background:color-mix(in srgb,var(--p-list-surface) 92%,var(--md-sys-color-primary) 8%);color:var(--p-list-foreground);font-weight:500}.praxis-list-root mat-list,.praxis-list-root mat-selection-list{display:block;padding-bottom:12px}.praxis-list-root .mat-divider{margin-left:var(--p-list-item-padding-x);margin-right:var(--p-list-item-padding-x)}.item-expansion{margin:0 var(--p-list-item-padding-x) var(--p-list-item-stack-gap);padding:var(--p-list-expansion-padding, 14px 16px);background:var(--md-sys-color-surface-container-low, var(--p-list-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:calc(var(--p-list-radius) * .75)}.item-expansion--detached,.item-expansion--modal{margin-top:var(--p-list-item-stack-gap);background:var(--md-sys-color-surface-container, var(--p-list-surface));box-shadow:var(--md-sys-elevation-level1)}.item-expansion--modal{border-color:var(--md-sys-color-outline);box-shadow:var(--md-sys-elevation-level3)}.item-expansion-grid{display:grid;grid-template-columns:var(--p-list-expansion-columns, repeat(auto-fit, minmax(220px, 1fr)));gap:var(--p-list-expansion-gap, 16px)}.expansion-section{min-width:0;display:grid;gap:10px;align-content:start}.expansion-section-title{font-size:.78rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--p-list-foreground-muted)}.expansion-info-list,.expansion-timeline,.expansion-key-value{display:grid;gap:10px}.expansion-info-item,.expansion-timeline-item{display:grid;gap:4px;padding:10px 12px;border-radius:14px;background:var(--p-list-item-surface);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 72%,transparent 28%)}.expansion-info-title,.expansion-timeline-title{font-size:.92rem;font-weight:600;color:var(--p-list-foreground)}.expansion-info-value,.expansion-timeline-meta,.expansion-timeline-description,.expansion-empty{font-size:.82rem;line-height:1.45;color:var(--p-list-foreground-muted)}.expansion-chip-list{display:flex;flex-wrap:wrap;gap:8px}.expansion-chip{background:var(--p-list-item-surface);border:1px solid var(--md-sys-color-outline-variant)}.expansion-key-value{margin:0}.expansion-key-value-row{display:grid;gap:4px;padding:4px 0 10px;border-bottom:1px solid var(--md-sys-color-outline-variant)}.expansion-key-value-row dt,.expansion-key-value-row dd{margin:0}.expansion-key-value-row dt{font-size:.78rem;font-weight:600;color:var(--p-list-foreground-muted)}.expansion-key-value-row dd{font-size:.9rem;color:var(--p-list-foreground)}.cards-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,max(340px,var(--p-list-tile-minW))),1fr));gap:max(var(--p-list-tile-gap),16px)}.tiles-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(var(--p-list-tile-minW),1fr));gap:var(--p-list-tile-gap)}.item-tile{position:relative;display:flex;flex-direction:column;gap:10px;padding:var(--p-list-tile-padding);border-radius:var(--p-list-tile-radius);background:var(--p-list-surface);border:var(--p-list-border);color:var(--p-list-foreground);cursor:pointer;min-height:160px;transition:box-shadow .16s ease-out,border-color .16s ease-out,transform .12s ease-out,background .16s ease-out}.item-tile:hover{box-shadow:var(--md-sys-elevation-level2);border-color:var(--md-sys-color-outline-variant);background:var(--p-list-item-hover-surface)}.item-tile:active{transform:scale(var(--p-list-tile-press-scale));background:var(--p-list-item-active-surface)}.tile-main-action{display:flex;flex:1;min-width:0;flex-direction:column;gap:10px;color:inherit;cursor:pointer}.tile-main-action:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px;border-radius:calc(var(--p-list-tile-radius) - 4px)}.tile-media{width:100%;aspect-ratio:var(--p-list-tile-media-ratio);border-radius:var(--p-list-tile-media-radius);background:var(--p-list-item-surface);border:1px solid var(--md-sys-color-outline-variant);display:grid;place-items:center;overflow:hidden}.tile-media img{width:100%;height:100%;object-fit:cover;display:block}.tile-media mat-icon{font-size:28px;height:28px;width:28px;color:var(--p-list-foreground-muted)}.tile-body{display:grid;gap:6px;min-width:0}.tile-meta{display:inline-flex;align-items:center;gap:6px;font-size:.75rem;color:var(--p-list-foreground-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tile-trailing{font-size:.75rem;color:var(--p-list-foreground-muted);white-space:nowrap}.tiles-features{display:flex;flex-wrap:wrap;gap:8px;color:inherit}.tile-status{position:absolute;top:10px;right:10px}.tile-actions{position:absolute;top:8px;left:8px;display:flex;gap:4px;opacity:0;pointer-events:none;transform:translateY(-2px);transition:opacity .16s ease-out,transform .16s ease-out}.item-tile:hover .tile-actions,.item-tile:focus-within .tile-actions{opacity:1;pointer-events:auto;transform:translateY(0)}@media(hover:none){.tile-actions{opacity:1;pointer-events:auto;transform:none}}@media(max-width:600px){.tiles-grid{grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}}@media(prefers-reduced-motion:reduce){.item-tile,.tile-actions{transition:none}.item-tile:active{transform:none}.item-card{transition:none}.item-card:active{transform:none}}.item-card{padding:max(var(--p-list-tile-padding),16px);display:flex;flex-direction:column;gap:var(--p-list-item-gap);min-height:220px;position:relative;cursor:pointer;background:var(--p-list-surface);border:var(--p-list-border);border-radius:var(--p-list-tile-radius);box-shadow:var(--p-list-shadow);transition:box-shadow .16s ease-out,border-color .16s ease-out,transform .12s ease-out,background .16s ease-out;--p-list-leading-width: 96px;--p-list-trailing-min-width: 0px}.card-main-action{display:block;width:100%;min-width:0;padding:0;border:0;background:transparent;color:inherit;text-align:start;font:inherit;cursor:pointer}.card-main-action:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px;border-radius:calc(var(--p-list-tile-radius) - 4px)}.item-card .list-item-content{display:block;padding:0;min-width:0;overflow:hidden}.item-card .list-item-leading{position:absolute;top:18px;left:18px;min-height:0;z-index:1}.item-card .leading-placeholder{width:36px;height:36px}.item-card .leading-icon.mat-icon,.item-card .list-item-leading>mat-icon{width:36px;height:36px;line-height:36px;font-size:20px;display:inline-flex;align-items:center;justify-content:center;border-radius:14px;background:linear-gradient(145deg,color-mix(in srgb,var(--md-sys-color-primary) 18%,transparent),color-mix(in srgb,var(--md-sys-color-primary-container) 34%,transparent));color:var(--md-sys-color-primary)}.item-card .list-item-text{display:grid;gap:9px;padding-left:50px;width:100%;min-width:0;box-sizing:border-box}.item-card .primary{padding-right:min(50%,160px);font-size:1rem;font-weight:800;letter-spacing:-.01em;line-height:1.18;min-width:0;overflow-wrap:anywhere}.item-card .secondary{color:var(--p-list-foreground-muted);font-weight:600;line-height:1.35;min-width:0;overflow-wrap:anywhere}.item-card .features{display:flex;flex-direction:column;gap:8px;margin-top:2px}.item-card .feature{display:inline-flex;align-items:start;gap:8px;min-width:0;max-width:100%;overflow-wrap:anywhere}.item-card .feature mat-icon{margin-top:1px;color:var(--p-list-foreground-muted);opacity:.86}.item-card .list-item-trailing{min-width:var(--p-list-trailing-min-width);grid-column:1/-1;align-items:stretch;padding-right:0}.card-detail-strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(148px,1fr));gap:10px;margin-top:16px}.card-detail{min-width:0;padding:11px 12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent);border-radius:14px;background:linear-gradient(145deg,color-mix(in srgb,var(--md-sys-color-primary-container) 22%,transparent),color-mix(in srgb,var(--p-list-surface) 92%,transparent));box-shadow:inset 0 1px #ffffff14}.card-detail__label{display:block;margin-bottom:3px;color:var(--p-list-foreground-muted);font-size:.68rem;font-weight:700;letter-spacing:.08em;line-height:1.1;text-transform:uppercase}.card-detail__value{display:block;min-width:0;color:var(--p-list-foreground);font-size:.94rem;font-weight:800;line-height:1.2;overflow-wrap:normal;word-break:normal}.card-detail__value,.card-detail__value *{white-space:nowrap}.card-detail--balance .card-detail__value{color:var(--md-sys-color-primary)}.card-detail--limit .card-detail__value{color:var(--md-sys-color-error)}.card-detail--risk .card-detail__value{color:var(--md-sys-color-tertiary, var(--p-list-foreground))}.item-card:hover{box-shadow:var(--md-sys-elevation-level3);border-color:var(--md-sys-color-outline-variant);background:var(--p-list-item-hover-surface)}.item-card:active{transform:scale(var(--p-list-tile-press-scale));background:var(--p-list-item-active-surface)}.card-actions{display:flex;align-items:center;justify-content:flex-end;gap:4px;padding:6px 8px 4px;border-top:1px solid var(--md-sys-color-outline-variant);margin-top:auto}.status-overlay{position:absolute;top:14px;right:14px;max-width:min(42%,148px);overflow:hidden;pointer-events:none}.status-overlay .mat-mdc-chip{pointer-events:auto;width:100%;min-width:0;max-width:100%}.status-overlay .mdc-evolution-chip__text-label,.status-overlay .mat-mdc-chip-action-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.leading-icon.mat-icon{width:36px;height:36px;line-height:36px;font-size:20px;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.mat-mdc-chip{--mdc-chip-container-height: var(--p-list-chip-height);font-size:var(--p-list-chip-font-size)}.meta .mat-icon{font-size:18px;height:18px;width:18px}.features{overflow:hidden}@keyframes shimmer{0%{background-position:-468px 0}to{background-position:468px 0}}.skeleton{animation-duration:1.2s;animation-fill-mode:forwards;animation-iteration-count:infinite;animation-name:shimmer;animation-timing-function:linear;background:linear-gradient(to right,var(--md-sys-color-surface-container-low) 8%,var(--md-sys-color-surface-container) 18%,var(--md-sys-color-surface-container-low) 33%);background-size:800px 104px;position:relative}.skeleton-avatar{width:32px;height:32px;border-radius:50%}.skeleton-line{height:10px;margin:6px 0;border-radius:6px}.skeleton-chip{width:48px;height:18px;border-radius:999px}.chip-outlined.mat-mdc-chip{background:transparent!important;border:1px solid currentColor}.w-60{width:60%}.w-40{width:40%}.list-collection-toolbar{display:flex;align-items:center;gap:12px;padding:12px;margin-bottom:8px;color:var(--p-list-foreground);background:var(--p-list-surface-low);border:var(--p-list-border);border-radius:calc(var(--p-list-radius) * .75)}.list-toolbar-search{flex:1 1 320px;min-width:220px;max-width:440px}.list-toolbar-commands{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:8px;margin-inline-start:auto}.list-toolbar-commands button{border-radius:8px}.list-toolbar-commands button .mat-icon{margin-right:6px}.list-toolbar-sort{width:200px}.list-remote-error{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:12px;margin:8px 4px 0;padding:16px;color:var(--md-sys-color-on-error-container);background:var(--md-sys-color-error-container);border-radius:calc(var(--p-list-radius) * .75)}.list-remote-error__copy{display:flex;flex-direction:column;gap:4px;min-width:0}.list-remote-error__copy span{color:color-mix(in srgb,var(--md-sys-color-on-error-container) 82%,transparent);line-height:1.45}.mat-mdc-paginator{margin-top:14px;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--p-list-surface-low);color:var(--p-list-foreground)}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-container{min-height:40px;padding:4px 8px;gap:6px}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-page-size-select{margin-left:8px;width:92px}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-page-size-select .mat-mdc-text-field-wrapper{--mdc-outlined-text-field-container-color: color-mix( in srgb, var(--p-list-surface-low) 92%, var(--md-sys-color-on-surface) 8% );--mdc-outlined-text-field-input-text-color: var(--p-list-foreground);min-height:40px;padding-inline:12px 8px;background:color-mix(in srgb,var(--p-list-surface-low) 92%,var(--md-sys-color-on-surface) 8%)!important;background-color:color-mix(in srgb,var(--p-list-surface-low) 92%,var(--md-sys-color-on-surface) 8%)!important;color:var(--p-list-foreground)!important}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-page-size-select .mat-mdc-form-field-infix{min-height:40px;padding-block:8px;width:auto}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-page-size-select .mat-mdc-select-trigger{min-height:24px;padding-right:2px;color:var(--p-list-foreground)}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-range-label,:host ::ng-deep .mat-mdc-paginator .mat-mdc-select-value-text{font-size:12px;color:var(--p-list-foreground-muted)}.list-paginator-total{margin-top:14px;padding:4px 8px 0;font-size:12px;color:var(--p-list-foreground-muted)}:host ::ng-deep .mat-mdc-paginator.hide-range .mat-mdc-paginator-range-label{display:none}::ng-deep .praxis-list-paginator-select-panel.mat-mdc-select-panel{--mat-select-panel-background-color: var( --md-sys-color-surface-container, #fff );background:var(--md-sys-color-surface-container, #fff)!important;background-color:var(--md-sys-color-surface-container, #fff)!important;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #d7dbe5) 70%,transparent);box-shadow:0 12px 28px color-mix(in srgb,var(--md-sys-color-shadow, #000) 28%,transparent)}::ng-deep .praxis-list-paginator-select-panel .mat-mdc-option{--mat-option-label-text-color: var(--md-sys-color-on-surface, #111827);color:var(--md-sys-color-on-surface, #111827)!important}::ng-deep .praxis-list-paginator-select-panel .mat-mdc-option:hover:not(.mdc-list-item--disabled),::ng-deep .praxis-list-paginator-select-panel .mat-mdc-option.mat-mdc-option-active{--mat-option-hover-state-layer-color: color-mix( in srgb, var(--md-sys-color-primary, #2f7cff) 14%, transparent );background:color-mix(in srgb,var(--md-sys-color-primary, #2f7cff) 14%,transparent)!important;color:var(--md-sys-color-on-surface, #111827)!important}.muted{color:var(--p-list-foreground-muted)}@container (max-width: 720px){.list-item-content{grid-template-columns:var(--p-list-leading-width) minmax(0,1fr)}.list-item-content--row-layout{grid-template-columns:var(--p-list-leading-width) minmax(0,1fr) max-content;grid-auto-flow:row;align-items:start}.list-item-content--row-layout .list-row-slot--leading{grid-column:1;grid-row:1}.list-item-content--row-layout .list-row-slot--primary,.list-item-content--row-layout .list-row-slot--identity{grid-column:2/-1;grid-row:1}.list-item-content--row-layout .list-row-slot--secondary{grid-column:2/-1;grid-row:2}.list-item-content--row-layout :is(.list-row-slot--primary,.list-row-slot--secondary){white-space:normal;overflow-wrap:anywhere}.list-item-content--row-layout .list-row-slot--meta{grid-column:2;grid-row:3;justify-content:flex-start}.list-item-content--row-layout .list-row-slot--trailing{grid-column:3;grid-row:3;justify-content:flex-end}.list-item-content--row-layout :is(.list-row-slot--balance,.list-row-slot--limit,.list-row-slot--risk,.list-row-slot--alerts,.list-row-slot--owner,.list-row-slot--actions,.list-row-slot--expand){grid-column:1/-1}.list-item-trailing{grid-column:1/-1;display:flex;flex-wrap:wrap;justify-content:flex-start;padding-right:0;align-items:center;text-align:start}.list-item-trailing .list-item-actions{margin-inline-start:auto}.item-expansion{margin-left:0;margin-right:0}.list-collection-toolbar{flex-direction:column;align-items:stretch}.list-toolbar-search,.list-toolbar-sort{flex:0 0 auto;width:100%;max-width:none;min-width:0}.list-toolbar-commands{width:100%;margin-inline-start:0}.list-toolbar-sort{flex:1 1 100%}.list-toolbar-commands button{flex:0 0 auto}.list-remote-error{grid-template-columns:auto minmax(0,1fr)}.list-remote-error button{grid-column:1/-1;justify-self:stretch}}@media(max-width:720px){.list-item-content{grid-template-columns:var(--p-list-leading-width) minmax(0,1fr)}.list-item-content--row-layout{grid-template-columns:var(--p-list-leading-width) minmax(0,1fr) max-content;grid-auto-flow:row;align-items:start}.list-item-content--row-layout .list-row-slot--leading{grid-column:1;grid-row:1}.list-item-content--row-layout .list-row-slot--primary,.list-item-content--row-layout .list-row-slot--identity{grid-column:2/-1;grid-row:1}.list-item-content--row-layout .list-row-slot--secondary{grid-column:2/-1;grid-row:2}.list-item-content--row-layout :is(.list-row-slot--primary,.list-row-slot--secondary){white-space:normal;overflow-wrap:anywhere}.list-item-content--row-layout .list-row-slot--meta{grid-column:2;grid-row:3;justify-content:flex-start}.list-item-content--row-layout .list-row-slot--trailing{grid-column:3;grid-row:3;justify-content:flex-end}.list-item-content--row-layout :is(.list-row-slot--balance,.list-row-slot--limit,.list-row-slot--risk,.list-row-slot--alerts,.list-row-slot--owner,.list-row-slot--actions,.list-row-slot--expand){grid-column:1/-1}.list-item-trailing{grid-column:1/-1;display:flex;flex-wrap:wrap;justify-content:flex-start;padding-right:0;align-items:center;text-align:start}.list-item-trailing .list-item-actions{margin-inline-start:auto}.item-expansion{margin-left:0;margin-right:0}.list-collection-toolbar{flex-direction:column;align-items:stretch}.list-toolbar-search,.list-toolbar-sort{flex:0 0 auto;width:100%;max-width:none;min-width:0}.list-toolbar-commands{width:100%;margin-inline-start:0}.list-toolbar-sort{flex:1 1 100%}.list-toolbar-commands button{flex:0 0 auto}.list-remote-error{grid-template-columns:auto minmax(0,1fr)}.list-remote-error button{grid-column:1/-1;justify-self:stretch}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i3.MatList, selector: "mat-list", exportAs: ["matList"] }, { kind: "component", type: i3.MatSelectionList, selector: "mat-selection-list", inputs: ["color", "compareWith", "multiple", "hideSingleSelectionIndicator", "disabled"], outputs: ["selectionChange"], exportAs: ["matSelectionList"] }, { kind: "component", type: i3.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "component", type: i3.MatListOption, selector: "mat-list-option", inputs: ["togglePosition", "checkboxPosition", "color", "value", "selected"], outputs: ["selectedChange"], exportAs: ["matListOption"] }, { kind: "component", type: i3.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatDividerModule }, { 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"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i5.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["role", "id", "aria-label", "aria-description", "value", "color", "removable", "highlighted", "disableRipple", "disabled"], outputs: ["removed", "destroyed"], exportAs: ["matChip"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4$2.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: i4$2.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i5$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: i5$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i5$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: MatProgressSpinnerModule }, { kind: "component", type: i8$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i10$1.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3$1.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: MatTooltipModule }, { kind: "directive", type: i12.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: PraxisAiAssistantShellComponent, selector: "praxis-ai-assistant-shell", inputs: ["labels", "mode", "state", "contextItems", "attachments", "messages", "quickReplies", "recommendedIntents", "prompt", "statusText", "errorText", "testIdPrefix", "panelTestId", "submitTestId", "applyTestId", "primaryAction", "secondaryActions", "governanceActions", "busy", "canSubmit", "canApply", "submitOnEnter", "showAttachAction", "enablePastedAttachments", "enableFileAttachments", "attachmentAccept", "attachmentMultiple", "voiceInputMode", "voiceLanguage", "draggable", "resizable", "minWidth", "minHeight", "margin", "layout"], outputs: ["promptChange", "submitPrompt", "apply", "retryTurn", "cancelTurn", "shellAction", "close", "attach", "attachmentsPasted", "attachmentsSelected", "removeAttachment", "messageAction", "editMessage", "resendMessage", "quickReply", "recommendedIntent", "layoutChange"] }, { kind: "component", type: ExecutiveBadgeComponent, selector: "praxis-executive-badge", inputs: ["ariaLabel", "label", "variant"] }, { kind: "component", type: ExecutiveAlertsComponent, selector: "praxis-executive-alerts", inputs: ["alerts"] }, { kind: "component", type: ExecutiveOwnerComponent, selector: "praxis-executive-owner", inputs: ["name"] }, { kind: "component", type: PraxisRichContent, selector: "praxis-rich-content", inputs: ["document", "nodes", "context", "hostCapabilities", "layout", "rootClassName"] }, { kind: "component", type: PraxisListMetricComponent, selector: "praxis-list-metric", inputs: ["metric"] }, { kind: "component", type: PraxisListComposeComponent, selector: "praxis-list-compose", inputs: ["compose"] }, { kind: "component", type: PraxisListRuntimeComponentComponent, selector: "praxis-list-runtime-component", inputs: ["component"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
15647
|
+
], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"praxis-list-root\"\n [ngClass]=\"skinClasses\"\n [attr.data-skin-scope]=\"skinScopeId\"\n [attr.aria-label]=\"config.a11y?.ariaLabel || null\"\n [attr.aria-labelledby]=\"config.a11y?.ariaLabelledBy || null\"\n>\n @if (inlineCss) {\n <style [attr.nonce]=\"cspNonce || null\" [textContent]=\"inlineCss\"></style>\n }\n\n @if (enableCustomization) {\n <div class=\"list-assistant\">\n <button\n mat-mini-fab\n type=\"button\"\n color=\"primary\"\n (click)=\"openConfigEditor()\"\n [matTooltip]=\"configEditorLabel()\"\n [attr.aria-label]=\"configEditorLabel()\"\n data-testid=\"praxis-list-open-config-editor\"\n >\n <mat-icon [praxisIcon]=\"'edit'\"></mat-icon>\n </button>\n <button\n mat-mini-fab\n type=\"button\"\n color=\"primary\"\n (click)=\"openAiAssistant()\"\n matTooltip=\"Copiloto semantico Praxis\"\n aria-label=\"Abrir copiloto semantico Praxis da lista\"\n data-testid=\"praxis-list-ai-assistant-trigger\"\n >\n <mat-icon [praxisIcon]=\"'auto_awesome'\"></mat-icon>\n </button>\n </div>\n }\n\n @if (aiAssistantOpen && aiAssistantViewState) {\n <praxis-ai-assistant-shell\n [labels]=\"aiAssistantLabels\"\n [mode]=\"aiAssistantViewState.mode\"\n [state]=\"aiAssistantViewState.state\"\n [contextItems]=\"aiAssistantViewState.contextItems\"\n [attachments]=\"aiAssistantViewState.attachments\"\n [messages]=\"aiAssistantViewState.messages\"\n [quickReplies]=\"aiAssistantViewState.quickReplies\"\n [prompt]=\"aiAssistantPrompt\"\n [statusText]=\"aiAssistantViewState.statusText\"\n [errorText]=\"aiAssistantViewState.errorText\"\n [busy]=\"\n aiAssistantViewState.state === 'processing' ||\n aiAssistantViewState.state === 'applying'\n \"\n [canApply]=\"aiAssistantViewState.canApply\"\n [layout]=\"aiAssistantLayout\"\n testIdPrefix=\"praxis-list-ai-assistant\"\n panelTestId=\"praxis-list-ai-assistant-panel\"\n submitTestId=\"praxis-list-ai-assistant-submit\"\n applyTestId=\"praxis-list-ai-assistant-apply\"\n (promptChange)=\"onAiAssistantPromptChange($event)\"\n (submitPrompt)=\"onAiAssistantSubmit($event)\"\n (apply)=\"onAiAssistantApply()\"\n (retryTurn)=\"onAiAssistantRetry()\"\n (cancelTurn)=\"onAiAssistantCancel()\"\n (quickReply)=\"onAiAssistantQuickReply($event)\"\n (editMessage)=\"onAiAssistantEditMessage($event)\"\n (resendMessage)=\"onAiAssistantResendMessage($event)\"\n (layoutChange)=\"onAiAssistantLayoutChange($event)\"\n (close)=\"closeAiAssistant()\"\n ></praxis-ai-assistant-shell>\n }\n\n <!-- Skeleton while loading -->\n @if ((loading$ | async) && hasSkeleton()) {\n @if (isListVariant()) {\n <mat-list>\n @for (_ of skeletonItems(); track $index; let i = $index) {\n <mat-list-item>\n <div class=\"list-item-content\">\n <div class=\"list-item-leading\">\n <div class=\"skeleton skeleton-avatar\"></div>\n </div>\n <div class=\"list-item-text\">\n <div class=\"skeleton skeleton-line w-60\"></div>\n @if (layoutLines > 1) {\n <div class=\"skeleton skeleton-line w-40\"></div>\n }\n </div>\n <div class=\"list-item-trailing\">\n <div class=\"skeleton skeleton-chip\"></div>\n </div>\n </div>\n </mat-list-item>\n }\n </mat-list>\n } @else {\n <div class=\"cards-grid\">\n @for (_ of skeletonItems(); track $index; let i = $index) {\n <div class=\"item-card\">\n <div class=\"list-item-content\">\n <div class=\"list-item-leading\">\n <div class=\"skeleton skeleton-avatar\"></div>\n </div>\n <div class=\"list-item-text\">\n <div class=\"skeleton skeleton-line w-60\"></div>\n @if (layoutLines > 1) {\n <div class=\"skeleton skeleton-line w-40\"></div>\n }\n </div>\n <div class=\"list-item-trailing\">\n <div class=\"skeleton skeleton-chip\"></div>\n </div>\n </div>\n </div>\n }\n </div>\n }\n } @else {\n <ng-container *ngTemplateOutlet=\"notLoading\"></ng-container>\n }\n\n <ng-template #notLoading>\n @if (\n config.export?.enabled ||\n (isRemoteDataMode() &&\n (config.ui?.showSort ||\n (config.ui?.showSearch && config.ui?.searchField)))\n ) {\n <div\n class=\"list-collection-toolbar\"\n role=\"group\"\n [attr.aria-label]=\"t('toolbar.label', 'Controles da lista')\"\n >\n @if (\n isRemoteDataMode() && config.ui?.showSearch && config.ui?.searchField\n ) {\n <mat-form-field\n class=\"list-toolbar-search\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n >\n <mat-label>{{\n config.ui?.searchPlaceholder || t(\"toolbar.search\", \"Buscar\")\n }}</mat-label>\n <mat-icon matPrefix [praxisIcon]=\"'search'\"></mat-icon>\n <input\n matInput\n type=\"search\"\n (input)=\"onSearchInput($any($event.target).value)\"\n />\n </mat-form-field>\n }\n\n <div class=\"list-toolbar-commands\">\n @if (isRemoteDataMode() && config.ui?.showSort) {\n <mat-form-field\n class=\"list-toolbar-sort\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n >\n <mat-label>{{ t(\"toolbar.sort\", \"Ordenar por\") }}</mat-label>\n @let activeSort = activeSortValue((page$ | async)?.sort);\n <mat-select\n [value]=\"activeSort\"\n (selectionChange)=\"onSortChange($event.value)\"\n >\n @if (activeSort && !hasSortOption(activeSort)) {\n <mat-option [value]=\"activeSort\">{{ activeSort }}</mat-option>\n }\n @for (op of config.ui?.sortOptions || []; track $index) {\n <mat-option [value]=\"sortOptionValue(op)\">{{\n sortOptionLabel(op)\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n\n @if (config.export?.enabled) {\n <button\n mat-stroked-button\n type=\"button\"\n [matMenuTriggerFor]=\"listExportMenu\"\n [attr.aria-label]=\"t('export.button', 'Exportar')\"\n [disabled]=\"exportBusy\"\n [attr.aria-busy]=\"exportBusy\"\n >\n <mat-icon [praxisIcon]=\"'download'\"></mat-icon>\n {{ t(\"export.button\", \"Exportar\") }}\n </button>\n <span class=\"cdk-visually-hidden\" aria-live=\"polite\">\n {{ exportStatusMessage }}\n </span>\n <mat-menu #listExportMenu=\"matMenu\">\n @for (format of listExportFormats(); track format) {\n <button\n mat-menu-item\n type=\"button\"\n (click)=\"onExportAction(format)\"\n >\n <mat-icon [praxisIcon]=\"exportIcon(format)\"></mat-icon>\n {{ format.toUpperCase() }}\n </button>\n }\n </mat-menu>\n }\n </div>\n </div>\n }\n\n @if (error$ | async; as remoteError) {\n <div class=\"list-remote-error\" role=\"alert\">\n <mat-icon [praxisIcon]=\"'error_outline'\"></mat-icon>\n <div class=\"list-remote-error__copy\">\n <strong>{{\n t(\"remoteError.title\", \"N\u00E3o foi poss\u00EDvel carregar a lista\")\n }}</strong>\n <span>\n {{\n remoteError.filtered\n ? t(\n \"remoteError.filteredDescription\",\n \"Os filtros permanecem aplicados. Nenhum dado sem filtro foi exibido.\"\n )\n : t(\n \"remoteError.description\",\n \"Verifique a conex\u00E3o e tente novamente.\"\n )\n }}\n </span>\n </div>\n <button mat-stroked-button type=\"button\" (click)=\"retryRemoteData()\">\n {{ t(\"remoteError.retry\", \"Tentar novamente\") }}\n </button>\n </div>\n }\n\n <!-- Empty state -->\n @if (items$ | async; as all) {\n @if (all.length === 0 && !(error$ | async)) {\n <div\n class=\"section-header\"\n [class.section-header--setup]=\"isResourceConnectionMissing()\"\n >\n @if (emptyStateTemplate(); as empty) {\n @if (\n simpleRichContentNodes(empty, {\n imageAlt: config.templating?.emptyState?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content [nodes]=\"richNodes\"></praxis-rich-content>\n } @else {\n @switch (empty.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"empty.value\"\n [ngClass]=\"empty.class\"\n [color]=\"\n isThemeColor(empty.color) ? empty.color : undefined\n \"\n [style.cssText]=\"\n (empty.style ? empty.style + ';' : '') +\n iconStyle(empty.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <span\n class=\"inline-image\"\n [ngClass]=\"empty.class\"\n [style.cssText]=\"empty.style\"\n >\n <img\n [src]=\"empty.value\"\n [alt]=\"config.templating?.emptyState?.imageAlt || ''\"\n />\n @if (empty.badge?.value) {\n <mat-chip\n class=\"inline-badge\"\n [color]=\"\n isThemeColor(empty.badge?.color)\n ? empty.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (empty.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(empty.badge?.color, empty.badge?.variant)\n \"\n >{{ empty.badge?.value }}</mat-chip\n >\n }\n </span>\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(empty.color) ? empty.color : undefined\n \"\n [ngClass]=\"[\n empty.class || '',\n (empty.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : '',\n ]\"\n [style.cssText]=\"\n chipStyle(empty.color, empty.variant) +\n (empty.style ? ';' + empty.style : '')\n \"\n >{{ empty.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n <span [ngClass]=\"empty.class\" [style.cssText]=\"empty.style\">\n @for (\n _ of ratingRange(empty);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, empty.value)\"\n [color]=\"ratingThemeColor(empty)\"\n [style.cssText]=\"ratingIconStyle(empty)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"empty.class\"\n [style.cssText]=\"empty.style\"\n [innerHTML]=\"empty.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"empty\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose [compose]=\"empty\"></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"empty\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span [ngClass]=\"empty.class\" [style.cssText]=\"empty.style\">{{\n empty.value\n }}</span>\n }\n }\n }\n }\n </div>\n }\n }\n\n <ng-template\n #rowLayoutNode\n let-node\n let-slot=\"slot\"\n let-imageAlt=\"imageAlt\"\n >\n @if (\n simpleRichContentNodes(node, { slot: slot, imageAlt: imageAlt });\n as richNodes\n ) {\n <praxis-rich-content [nodes]=\"richNodes\"></praxis-rich-content>\n } @else {\n @switch (node?.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"node?.value\"\n [ngClass]=\"node?.class\"\n [color]=\"isThemeColor(node?.color) ? node?.color : undefined\"\n [style.cssText]=\"\n (node?.style || '' ? node.style + ';' : '') +\n iconStyle(node?.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <span\n class=\"inline-image\"\n [ngClass]=\"node?.class\"\n [style.cssText]=\"node?.style\"\n >\n <img\n [src]=\"node?.value\"\n [alt]=\"node?.imageAlt || imageAlt || ''\"\n />\n @if (node?.badge?.value) {\n <mat-chip\n class=\"inline-badge\"\n [color]=\"\n isThemeColor(node?.badge?.color)\n ? node?.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (node?.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(node?.badge?.color, node?.badge?.variant)\n \"\n >\n {{ node?.badge?.value }}\n </mat-chip>\n }\n </span>\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"isThemeColor(node?.color) ? node?.color : undefined\"\n [ngClass]=\"[\n node?.class || '',\n (node?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : '',\n ]\"\n [style.cssText]=\"\n chipStyle(node?.color, node?.variant) +\n (node?.style ? ';' + node.style : '')\n \"\n >\n {{ node?.value }}\n </mat-chip>\n }\n @case (\"rating\") {\n <span [ngClass]=\"node?.class\" [style.cssText]=\"node?.style\">\n @for (_ of ratingRange(node); track $index; let idx = $index) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, node?.value)\"\n [color]=\"ratingThemeColor(node)\"\n [style.cssText]=\"ratingIconStyle(node)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"node?.class\"\n [style.cssText]=\"node?.style\"\n [innerHTML]=\"node?.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"node\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose [compose]=\"node\"></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"node\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span [ngClass]=\"node?.class\" [style.cssText]=\"node?.style\">\n @if (\n slot === \"meta\" && config.templating?.metaPrefixIcon;\n as mpi\n ) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n {{ node?.value }}\n </span>\n }\n }\n }\n </ng-template>\n\n <ng-template #featureContent let-item let-feature=\"feature\">\n @if (featureTemplateNode(item, feature); as featureNode) {\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: featureNode,\n slot: 'feature',\n imageAlt: feature?.imageAlt || '',\n }\n \"\n ></ng-container>\n } @else if (featureRichContentNodes(item, feature); as featureNodes) {\n <praxis-rich-content [nodes]=\"featureNodes\"></praxis-rich-content>\n } @else {\n @if (feature.icon && featuresMode() !== \"labels-only\") {\n <mat-icon [praxisIcon]=\"feature.icon\"></mat-icon>\n }\n @if (featuresMode() !== \"icons-only\") {\n <span [ngClass]=\"feature.class\" [style.cssText]=\"feature.style\">\n @for (line of featureLabelLines(item, feature.expr); track $index) {\n <span class=\"feature-line\">{{ line }}</span>\n }\n </span>\n }\n }\n @if (\n featureProgressPercent(item, feature.expr, feature.class);\n as progress\n ) {\n <span class=\"feature-progress\" aria-hidden=\"true\">\n <span\n class=\"feature-progress__value\"\n [style.width.%]=\"progress\"\n ></span>\n </span>\n }\n </ng-template>\n\n <ng-template\n #listRowLayout\n let-item\n let-index=\"index\"\n let-sectionKey=\"sectionKey\"\n let-clickable=\"clickable\"\n >\n <div\n class=\"list-item-content\"\n [ngClass]=\"rowLayoutItemClass(item)\"\n [attr.style]=\"rowLayoutItemStyle(item)\"\n >\n @if (clickable) {\n <button\n type=\"button\"\n class=\"list-row-main-action\"\n [attr.aria-label]=\"itemAriaLabel(item)\"\n [attr.aria-expanded]=\"\n expansionOwnedByRow() ? isExpanded(item, index) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByRow() ? expandRegionId(item, index) : null\n \"\n (click)=\"onRowActivate(item, index, sectionKey)\"\n ></button>\n }\n @for (\n column of rowLayoutColumns();\n track rowLayoutTrackColumn($index, column)\n ) {\n <div\n [attr.data-row-slot]=\"column.slot\"\n [ngClass]=\"rowLayoutColumnClass(column)\"\n [attr.style]=\"rowLayoutColumnStyle(column)\"\n >\n @if (column.slot === \"leading\") {\n @if (rowLayoutSlot(item, column.slot); as lead) {\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: lead,\n slot: column.slot,\n imageAlt: rowLayoutImageAlt(column.slot),\n }\n \"\n ></ng-container>\n } @else {\n <span class=\"leading-placeholder\"></span>\n }\n } @else if (column.slot === \"trailing\") {\n @if (rowLayoutSlot(item, column.slot); as trailingNode) {\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: trailingNode,\n slot: column.slot,\n imageAlt: rowLayoutImageAlt(column.slot),\n }\n \"\n ></ng-container>\n }\n @if (showExpandIcon(\"trailing\")) {\n <button\n type=\"button\"\n [praxisIconButton]=\"\n isExpanded(item, index) ? 'expand_less' : 'expand_more'\n \"\n size=\"compact\"\n class=\"expand-toggle\"\n [attr.aria-label]=\"expandToggleAriaLabel(item, index)\"\n [attr.aria-expanded]=\"\n expansionOwnedByIcon() ? isExpanded(item, index) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByIcon() ? expandRegionId(item, index) : null\n \"\n (click)=\"onExpandToggle($event, item, index)\"\n ></button>\n }\n @if ((visibleActions(item, \"trailing\") || []).length) {\n <div\n class=\"list-item-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(item, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, item, index)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"onActionClick($event, action.id, item, index)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'stroked')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'raised')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant || action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'flat')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n } @else if (column.slot === \"actions\") {\n @if ((visibleActions(item, \"actions\") || []).length) {\n <div\n class=\"list-item-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(item, \"actions\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, item, index)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"onActionClick($event, action.id, item, index)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'stroked')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'raised')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant || action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'flat')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n } @else if (column.slot === \"expand\") {\n @if (showExpandIcon(\"expand\")) {\n <button\n type=\"button\"\n [praxisIconButton]=\"\n isExpanded(item, index) ? 'expand_less' : 'expand_more'\n \"\n size=\"compact\"\n class=\"expand-toggle\"\n [attr.aria-label]=\"expandToggleAriaLabel(item, index)\"\n [attr.aria-expanded]=\"\n expansionOwnedByIcon() ? isExpanded(item, index) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByIcon() ? expandRegionId(item, index) : null\n \"\n (click)=\"onExpandToggle($event, item, index)\"\n ></button>\n }\n } @else if (rowLayoutSlot(item, column.slot); as slotNode) {\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: slotNode,\n slot: column.slot,\n imageAlt: rowLayoutImageAlt(column.slot),\n }\n \"\n ></ng-container>\n }\n </div>\n }\n </div>\n </ng-template>\n\n <!-- List variant -->\n @if (isListVariant()) {\n <!-- Selection and row actions share one neutral list shell so every\n interactive affordance remains a sibling keyboard target. -->\n <ng-container *ngTemplateOutlet=\"readList\"></ng-container>\n\n <!-- Unified list surface -->\n <ng-template #readList>\n <mat-list\n role=\"list\"\n [attr.aria-label]=\"\n isSelectionEnabled()\n ? selectionListAriaLabel()\n : config.a11y?.ariaLabel || null\n \"\n [attr.aria-labelledby]=\"config.a11y?.ariaLabelledBy || null\"\n >\n @for (\n section of sections$ | async;\n track trackBySection($index, section);\n let sidx = $index\n ) {\n @if (section.key) {\n <div class=\"section-header mat-subheader\">\n @if (sectionHeaderTemplate(section.key); as sh) {\n @if (\n simpleRichContentNodes(sh, {\n imageAlt:\n config.templating?.sectionHeader?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (sh.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"sh.value\"\n [ngClass]=\"sh.class\"\n [color]=\"\n isThemeColor(sh.color) ? sh.color : undefined\n \"\n [style.cssText]=\"\n (sh.style ? sh.style + ';' : '') +\n iconStyle(sh.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <span\n class=\"inline-image\"\n [ngClass]=\"sh.class\"\n [style.cssText]=\"sh.style\"\n >\n <img\n [src]=\"sh.value\"\n [alt]=\"\n config.templating?.sectionHeader?.imageAlt || ''\n \"\n />\n @if (sh.badge?.value) {\n <mat-chip\n class=\"inline-badge\"\n [color]=\"\n isThemeColor(sh.badge?.color)\n ? sh.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (sh.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(sh.badge?.color, sh.badge?.variant)\n \"\n >{{ sh.badge?.value }}</mat-chip\n >\n }\n </span>\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(sh.color) ? sh.color : undefined\n \"\n [ngClass]=\"[\n sh.class || '',\n (sh.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : '',\n ]\"\n [style.cssText]=\"\n chipStyle(sh.color, sh.variant) +\n (sh.style ? ';' + sh.style : '')\n \"\n >{{ sh.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n <span [ngClass]=\"sh.class\" [style.cssText]=\"sh.style\">\n @for (\n _ of ratingRange(sh);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, sh.value)\"\n [color]=\"ratingThemeColor(sh)\"\n [style.cssText]=\"ratingIconStyle(sh)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"sh.class\"\n [style.cssText]=\"sh.style\"\n [innerHTML]=\"sh.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"sh\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"sh\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"sh\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span [ngClass]=\"sh.class\" [style.cssText]=\"sh.style\">{{\n sh.value\n }}</span>\n }\n }\n }\n }\n </div>\n }\n @for (\n item of section.items;\n track trackByItem($index, item);\n let i = $index\n ) {\n <mat-list-item\n role=\"listitem\"\n [attr.data-item-status]=\"item?.status || null\"\n >\n <div\n class=\"list-row-shell\"\n [class.list-row-shell--selectable]=\"isSelectionEnabled()\"\n [class.list-row-shell--selected]=\"isItemSelected(item)\"\n >\n @if (hasRowLayoutGrid()) {\n <ng-container\n *ngTemplateOutlet=\"\n listRowLayout;\n context: {\n $implicit: item,\n index: i,\n sectionKey: section.key || undefined,\n clickable: true,\n }\n \"\n ></ng-container>\n } @else {\n <div\n class=\"list-item-content\"\n [ngClass]=\"itemRuleClass(item)\"\n [attr.style]=\"itemRuleStyle(item)\"\n >\n <button\n type=\"button\"\n class=\"list-item-main-action\"\n [attr.aria-label]=\"itemAriaLabel(item)\"\n [attr.aria-expanded]=\"\n expansionOwnedByRow() ? isExpanded(item, i) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByRow() ? expandRegionId(item, i) : null\n \"\n (click)=\"\n onRowActivate(item, i, section.key || undefined)\n \"\n >\n <div class=\"list-item-leading\">\n @if (leading(item); as lead) {\n @if (\n simpleRichContentNodes(lead, {\n imageAlt:\n config.templating?.leading?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"lead.value\"\n [ngClass]=\"lead.class\"\n [color]=\"\n isThemeColor(lead.color)\n ? lead.color\n : undefined\n \"\n [style.cssText]=\"\n (lead.style ? lead.style + ';' : '') +\n iconStyle(lead.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >\n <img\n [src]=\"lead.value\"\n [alt]=\"\n config.templating?.leading?.imageAlt ||\n ''\n \"\n />\n @if (lead.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(lead.badge?.color)\n ? lead.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (lead.badge?.variant || 'filled') ===\n 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n lead.badge?.color,\n lead.badge?.variant\n )\n \"\n >{{ lead.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"text\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(lead.color)\n ? lead.color\n : undefined\n \"\n [ngClass]=\"\n (lead.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(lead.color, lead.variant)\n \"\n >{{ lead.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(lead);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, lead.value)\"\n [color]=\"ratingThemeColor(lead)\"\n [style.cssText]=\"ratingIconStyle(lead)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n [innerHTML]=\"lead.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"lead\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"lead\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"lead\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n }\n }\n } @else {\n <span class=\"leading-placeholder\"></span>\n }\n </div>\n <div class=\"list-item-text\">\n <div\n class=\"primary\"\n [ngClass]=\"primary(item)?.class\"\n [style.cssText]=\"primary(item)?.style\"\n >\n @if (\n simpleRichContentNodes(primary(item));\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (primary(item)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"primary(item)\"\n ></praxis-list-metric>\n } @else if (primary(item)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"primary(item)\"\n ></praxis-list-compose>\n } @else if (primary(item)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"primary(item)\"\n ></praxis-list-runtime-component>\n } @else if (primary(item)?.type === \"html\") {\n <span [innerHTML]=\"primary(item)?.value\"></span>\n } @else {\n {{ primary(item)?.value }}\n }\n </div>\n @if (layoutLines > 1) {\n <div\n class=\"secondary\"\n [ngClass]=\"secondary(item)?.class\"\n [style.cssText]=\"secondary(item)?.style\"\n >\n @if (\n simpleRichContentNodes(secondary(item));\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (\n item.segmentVariant &&\n item.segmentLabel &&\n item.accountType &&\n item.since\n ) {\n <!-- Component-based rendering for executive items -->\n <span class=\"exec-subline\">\n <praxis-executive-badge\n [variant]=\"item.segmentVariant\"\n [label]=\"item.segmentLabel\"\n ></praxis-executive-badge>\n <span class=\"exec-subcopy\">{{\n item.accountType\n }}</span>\n <span class=\"exec-subseparator\">\u00B7</span>\n <span class=\"exec-subcopy\"\n >Desde {{ item.since }}</span\n >\n </span>\n } @else if (secondary(item)?.type === \"html\") {\n <span\n [innerHTML]=\"secondary(item)?.value\"\n ></span>\n } @else if (secondary(item)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"secondary(item)\"\n ></praxis-list-metric>\n } @else if (secondary(item)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"secondary(item)\"\n ></praxis-list-compose>\n } @else if (\n secondary(item)?.type === \"component\"\n ) {\n <praxis-list-runtime-component\n [component]=\"secondary(item)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ secondary(item)?.value }}\n }\n </div>\n }\n @if (meta(item); as m) {\n @if (\n (config.templating?.metaPlacement === \"line\" &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")) ||\n (layoutLines > 2 &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\"))\n ) {\n <div\n class=\"tertiary\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (\n config.templating?.metaPrefixIcon;\n as mpi\n ) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n {{ m.value }}\n </div>\n }\n }\n @if (featuresVisible()) {\n <div class=\"features\">\n @for (\n f of config.templating?.features || [];\n track $index;\n let fi = $index\n ) {\n <span\n class=\"feature\"\n [ngClass]=\"\n featureSemanticClass(item, f.expr, f.class)\n \"\n >\n <ng-container\n *ngTemplateOutlet=\"\n featureContent;\n context: { $implicit: item, feature: f }\n \"\n ></ng-container>\n </span>\n }\n </div>\n }\n </div>\n </button>\n <div class=\"list-item-trailing\">\n @if (meta(item); as m) {\n @if (\n !(\n (config.templating?.metaPlacement === \"line\" ||\n layoutLines > 2) &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")\n )\n ) {\n <div\n class=\"meta\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (\n simpleRichContentNodes(m, { slot: \"meta\" });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(m.color)\n ? m.color\n : undefined\n \"\n [ngClass]=\"\n (m.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(m.color, m.variant)\n \"\n >{{ m.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(m);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, m.value)\"\n [color]=\"ratingThemeColor(m)\"\n [style.cssText]=\"ratingIconStyle(m)\"\n ></mat-icon>\n }\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"m.value\"\n [color]=\"\n isThemeColor(m.color)\n ? m.color\n : undefined\n \"\n [style.cssText]=\"iconStyle(m.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img\n [src]=\"m.value\"\n [alt]=\"m.imageAlt || ''\"\n /></span>\n }\n @case (\"html\") {\n <span [innerHTML]=\"m.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"m\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"m\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"m\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>\n @if (\n config.templating?.metaPrefixIcon;\n as mpi2\n ) {\n <mat-icon\n [praxisIcon]=\"mpi2\"\n ></mat-icon>\n }\n {{ m.value }}</span\n >\n }\n }\n }\n </div>\n }\n }\n @if (trailing(item); as tr) {\n <div\n class=\"trailing\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n @if (\n simpleRichContentNodes(tr, {\n imageAlt:\n config.templating?.trailing?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (tr.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr.color)\n ? tr.color\n : undefined\n \"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(tr.color, tr.variant)\n \"\n >{{ tr.value }}</mat-chip\n >\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"\n isThemeColor(tr.color)\n ? tr.color\n : undefined\n \"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n <img\n [src]=\"tr.value\"\n [alt]=\"\n config.templating?.trailing?.imageAlt ||\n ''\n \"\n />\n @if (tr.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(tr.badge?.color)\n ? tr.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (tr.badge?.variant || 'filled') ===\n 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n tr.badge?.color,\n tr.badge?.variant\n )\n \"\n >{{ tr.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(tr);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, tr.value)\"\n [color]=\"ratingThemeColor(tr)\"\n [style.cssText]=\"ratingIconStyle(tr)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n @if (\n item.alertsData !== undefined &&\n item.ownerName\n ) {\n <!-- Component-based rendering for executive trailing -->\n <span class=\"exec-trailing-shell\">\n <praxis-executive-alerts\n [alerts]=\"item.alertsData\"\n ></praxis-executive-alerts>\n <praxis-executive-owner\n [name]=\"item.ownerName\"\n ></praxis-executive-owner>\n </span>\n } @else {\n <span [innerHTML]=\"tr.value\"></span>\n }\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"tr\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"tr\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"tr\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>{{ tr.value }}</span>\n }\n }\n }\n </div>\n }\n @if (showExpandIcon(\"trailing\")) {\n <button\n type=\"button\"\n [praxisIconButton]=\"\n isExpanded(item, i)\n ? 'expand_less'\n : 'expand_more'\n \"\n size=\"compact\"\n class=\"expand-toggle\"\n [attr.aria-label]=\"expandToggleAriaLabel(item, i)\"\n [attr.aria-expanded]=\"\n expansionOwnedByIcon()\n ? isExpanded(item, i)\n : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByIcon()\n ? expandRegionId(item, i)\n : null\n \"\n (click)=\"onExpandToggle($event, item, i)\"\n ></button>\n }\n @if ((visibleActions(item, \"trailing\") || []).length) {\n <div\n class=\"list-item-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(item, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"\n action.icon || 'more_horiz'\n \"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, item, i)\"\n [style.cssText]=\"\n actionIconStyle(action.color)\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'stroked')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"\n action.label || action.id\n \"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'raised')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"\n action.label || action.id\n \"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant ||\n action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'flat')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"\n action.label || action.id\n \"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n </div>\n </div>\n }\n @if (isSelectionEnabled()) {\n <div class=\"list-selection-control\">\n @if (config.selection?.mode === \"multiple\") {\n <mat-checkbox\n [checked]=\"isItemSelected(item)\"\n [disabled]=\"boundControl.disabled\"\n [aria-label]=\"selectionControlAriaLabel(item)\"\n (change)=\"setItemSelected(item, $event.checked)\"\n ></mat-checkbox>\n } @else {\n <mat-radio-button\n [name]=\"selectionControlName()\"\n [checked]=\"isItemSelected(item)\"\n [disabled]=\"boundControl.disabled\"\n [aria-label]=\"selectionControlAriaLabel(item)\"\n (change)=\"setItemSelected(item, true)\"\n ></mat-radio-button>\n }\n </div>\n }\n </div>\n @if (isExpandable() && isExpanded(item, i)) {\n <div\n class=\"item-expansion\"\n [ngClass]=\"expansionRenderingClasses()\"\n [style]=\"expansionRenderingStyle()\"\n [id]=\"expandRegionId(item, i)\"\n role=\"region\"\n [attr.aria-label]=\"expansionRegionAriaLabel(item)\"\n >\n <div class=\"item-expansion-grid\">\n @for (\n expansionSection of expansionSections(item);\n track expansionSection.id\n ) {\n <section\n class=\"expansion-section\"\n [attr.data-section-type]=\"expansionSection.type\"\n >\n @if (expansionSection.title) {\n <div class=\"expansion-section-title\">\n {{ expansionSection.title }}\n </div>\n }\n @if (\n expansionSectionHasContent(\n item,\n expansionSection,\n i\n )\n ) {\n @switch (expansionSection.type) {\n @case (\"chip-list\") {\n <div class=\"expansion-chip-list\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <mat-chip class=\"expansion-chip\">{{\n expansionItemLabel(expansionEntry)\n }}</mat-chip>\n }\n </div>\n }\n @case (\"timeline\") {\n <div class=\"expansion-timeline\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-timeline-item\">\n <div class=\"expansion-timeline-title\">\n {{\n expansionTimelineTitle(expansionEntry)\n }}\n </div>\n @if (\n expansionTimelineMeta(expansionEntry)\n ) {\n <div class=\"expansion-timeline-meta\">\n {{\n expansionTimelineMeta(\n expansionEntry\n )\n }}\n </div>\n }\n @if (\n expansionTimelineDescription(\n expansionEntry\n )\n ) {\n <div\n class=\"expansion-timeline-description\"\n >\n {{\n expansionTimelineDescription(\n expansionEntry\n )\n }}\n </div>\n }\n </div>\n }\n </div>\n }\n @case (\"key-value\") {\n <dl class=\"expansion-key-value\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-key-value-row\">\n @if (\n expansionKeyValueKey(expansionEntry)\n ) {\n <dt>\n {{\n expansionKeyValueKey(expansionEntry)\n }}\n </dt>\n }\n <dd>\n {{\n expansionKeyValueValue(expansionEntry)\n }}\n </dd>\n </div>\n }\n </dl>\n }\n @case (\"rich-content\") {\n @if (\n expansionRichContentDocument(\n item,\n expansionSection,\n i\n );\n as richExpansionDocument\n ) {\n <praxis-rich-content\n [document]=\"richExpansionDocument\"\n [context]=\"\n expansionRichContentContext(\n item,\n expansionSection,\n i\n )\n \"\n [hostCapabilities]=\"\n expansionRichContentHostCapabilities(\n item,\n expansionSection,\n i\n )\n \"\n rootClassName=\"praxis-list-expansion-rich-content\"\n ></praxis-rich-content>\n }\n }\n @default {\n <div class=\"expansion-info-list\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-info-item\">\n <div class=\"expansion-info-title\">\n {{ expansionInfoTitle(expansionEntry) }}\n </div>\n @if (expansionInfoValue(expansionEntry)) {\n <div class=\"expansion-info-value\">\n {{\n expansionInfoValue(expansionEntry)\n }}\n </div>\n }\n </div>\n }\n </div>\n }\n }\n } @else {\n <div class=\"expansion-empty\">\n {{\n expansionSection.emptyLabel ||\n \"Sem dados dispon\u00EDveis\"\n }}\n </div>\n }\n </section>\n }\n </div>\n </div>\n }\n </mat-list-item>\n @if (\n (config.layout?.dividers === \"all\" ||\n config.layout?.dividers === \"between\") &&\n i < section.items.length - 1\n ) {\n <mat-divider\n role=\"presentation\"\n aria-hidden=\"true\"\n ></mat-divider>\n }\n }\n }\n </mat-list>\n </ng-template>\n } @else if (isTilesVariant()) {\n <ng-container *ngTemplateOutlet=\"tilesVariant\"></ng-container>\n } @else {\n <ng-container *ngTemplateOutlet=\"cardsVariant\"></ng-container>\n }\n\n <!-- Cards variant -->\n <ng-template #cardsVariant>\n <div class=\"cards-grid\">\n @for (it of (items$ | async) ?? []; track $index; let i = $index) {\n <div\n class=\"item-card\"\n [ngClass]=\"itemRuleClass(it)\"\n [attr.style]=\"itemRuleStyle(it)\"\n >\n <div\n class=\"list-item-content card-main-action\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"itemAriaLabel(it)\"\n (click)=\"onItemClick(it, i)\"\n (keydown.enter)=\"onItemClick(it, i)\"\n (keydown.space)=\"$event.preventDefault(); onItemClick(it, i)\"\n >\n <div class=\"list-item-leading\">\n @if (leading(it); as lead) {\n @if (\n simpleRichContentNodes(lead, {\n imageAlt: config.templating?.leading?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"lead.value\"\n [ngClass]=\"lead.class\"\n [color]=\"\n isThemeColor(lead.color) ? lead.color : undefined\n \"\n [style.cssText]=\"\n (lead.style ? lead.style + ';' : '') +\n iconStyle(lead.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >\n <img\n [src]=\"lead.value\"\n [alt]=\"config.templating?.leading?.imageAlt || ''\"\n />\n @if (lead.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(lead.badge?.color)\n ? lead.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (lead.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n lead.badge?.color,\n lead.badge?.variant\n )\n \"\n >{{ lead.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"text\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(lead.color) ? lead.color : undefined\n \"\n [ngClass]=\"\n (lead.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(lead.color, lead.variant)\"\n >{{ lead.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(lead);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, lead.value)\"\n [color]=\"ratingThemeColor(lead)\"\n [style.cssText]=\"ratingIconStyle(lead)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n [innerHTML]=\"lead.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"lead\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"lead\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"lead\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n }\n }\n } @else {\n <span class=\"leading-placeholder\"></span>\n }\n </div>\n <div class=\"list-item-text\">\n <div\n class=\"primary\"\n [ngClass]=\"primary(it)?.class\"\n [style.cssText]=\"primary(it)?.style\"\n >\n @if (simpleRichContentNodes(primary(it)); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (primary(it)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"primary(it)\"\n ></praxis-list-metric>\n } @else if (primary(it)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"primary(it)\"\n ></praxis-list-compose>\n } @else if (primary(it)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"primary(it)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ primary(it)?.value }}\n }\n </div>\n @if (layoutLines > 1) {\n <div\n class=\"secondary\"\n [ngClass]=\"secondary(it)?.class\"\n [style.cssText]=\"secondary(it)?.style\"\n >\n @if (simpleRichContentNodes(secondary(it)); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (secondary(it)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"secondary(it)\"\n ></praxis-list-metric>\n } @else if (secondary(it)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"secondary(it)\"\n ></praxis-list-compose>\n } @else if (secondary(it)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"secondary(it)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ secondary(it)?.value }}\n }\n </div>\n }\n @if (meta(it); as m) {\n @if (\n (config.templating?.metaPlacement === \"line\" &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")) ||\n (layoutLines > 2 &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\"))\n ) {\n <div\n class=\"tertiary\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (config.templating?.metaPrefixIcon; as mpi) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n {{ m.value }}\n </div>\n }\n }\n @if (featuresVisible()) {\n <div class=\"features\">\n @for (\n f of config.templating?.features || [];\n track $index;\n let fi = $index\n ) {\n <span\n class=\"feature\"\n [ngClass]=\"featureSemanticClass(it, f.expr, f.class)\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n featureContent;\n context: { $implicit: it, feature: f }\n \"\n ></ng-container>\n </span>\n }\n </div>\n }\n @if (cardDetailSlots(it); as detailSlots) {\n @if (detailSlots.length) {\n <div class=\"card-detail-strip\">\n @for (detail of detailSlots; track detail.slot) {\n <div\n class=\"card-detail\"\n [ngClass]=\"'card-detail--' + detail.slot\"\n >\n <span class=\"card-detail__label\">{{\n detail.label\n }}</span>\n <span class=\"card-detail__value\">\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: detail.node,\n slot: detail.slot,\n imageAlt: rowLayoutImageAlt(detail.slot),\n }\n \"\n ></ng-container>\n </span>\n </div>\n }\n </div>\n }\n }\n </div>\n <div class=\"list-item-trailing\">\n @if (meta(it); as m) {\n @if (\n !(\n (config.templating?.metaPlacement === \"line\" ||\n layoutLines > 2) &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")\n )\n ) {\n <div\n class=\"meta\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (\n simpleRichContentNodes(m, {\n slot: \"meta\",\n imageAlt: m.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(m.color) ? m.color : undefined\n \"\n [ngClass]=\"\n (m.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(m.color, m.variant)\"\n >{{ m.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(m);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, m.value)\"\n [color]=\"ratingThemeColor(m)\"\n [style.cssText]=\"ratingIconStyle(m)\"\n ></mat-icon>\n }\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"m.value\"\n [color]=\"\n isThemeColor(m.color) ? m.color : undefined\n \"\n [style.cssText]=\"iconStyle(m.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img [src]=\"m.value\" [alt]=\"m.imageAlt || ''\"\n /></span>\n }\n @case (\"html\") {\n <span [innerHTML]=\"m.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"m\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"m\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"m\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>\n @if (config.templating?.metaPrefixIcon; as mpi2) {\n <mat-icon [praxisIcon]=\"mpi2\"></mat-icon>\n }\n {{ m.value }}</span\n >\n }\n }\n }\n </div>\n }\n }\n @if (trailing(it); as tr) {\n @if (\n (config.templating?.statusPosition || \"inline\") ===\n \"top-right\" &&\n (tr?.type === \"chip\" || tr?.type === \"icon\")\n ) {\n <div class=\"status-overlay\">\n @if (simpleRichContentNodes(tr); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @if (tr?.type === \"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(tr.color, tr.variant)\"\n >{{ tr.value }}</mat-chip\n >\n }\n @if (tr?.type === \"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n }\n </div>\n } @else {\n <div\n class=\"trailing\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n @if (\n simpleRichContentNodes(tr, {\n imageAlt: config.templating?.trailing?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (tr.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(tr.color, tr.variant)\"\n >{{ tr.value }}</mat-chip\n >\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n <img\n [src]=\"tr.value\"\n [alt]=\"\n config.templating?.trailing?.imageAlt || ''\n \"\n />\n @if (tr.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(tr.badge?.color)\n ? tr.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (tr.badge?.variant || 'filled') ===\n 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n tr.badge?.color,\n tr.badge?.variant\n )\n \"\n >{{ tr.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(tr);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, tr.value)\"\n [color]=\"ratingThemeColor(tr)\"\n [style.cssText]=\"ratingIconStyle(tr)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n <span [innerHTML]=\"tr.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"tr\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"tr\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"tr\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>{{ tr.value }}</span>\n }\n }\n }\n </div>\n }\n }\n </div>\n </div>\n <div\n class=\"card-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(it, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, it, i)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'stroked')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'raised')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant || action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'flat')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n </div>\n }\n </div>\n </ng-template>\n\n <!-- Tiles variant -->\n <ng-template #tilesVariant>\n <div class=\"tiles-grid\">\n @for (it of (items$ | async) ?? []; track $index; let i = $index) {\n <div\n class=\"item-tile\"\n [ngClass]=\"itemRuleClass(it)\"\n [attr.style]=\"itemRuleStyle(it)\"\n >\n <div\n class=\"tile-main-action\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"itemAriaLabel(it)\"\n (click)=\"onItemClick(it, i)\"\n (keydown.enter)=\"onItemClick(it, i)\"\n (keydown.space)=\"$event.preventDefault(); onItemClick(it, i)\"\n >\n <div class=\"tile-media\">\n @if (leading(it); as lead) {\n @if (\n simpleRichContentNodes(lead, {\n imageAlt: config.templating?.leading?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case (\"image\") {\n <img\n [src]=\"lead.value\"\n [alt]=\"config.templating?.leading?.imageAlt || ''\"\n />\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"lead.value\"\n [ngClass]=\"lead.class\"\n [color]=\"\n isThemeColor(lead.color) ? lead.color : undefined\n \"\n [style.cssText]=\"\n (lead.style ? lead.style + ';' : '') +\n iconStyle(lead.color)\n \"\n ></mat-icon>\n }\n @case (\"text\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(lead.color) ? lead.color : undefined\n \"\n [ngClass]=\"\n (lead.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(lead.color, lead.variant)\"\n >{{ lead.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >\n @for (\n _ of ratingRange(lead);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, lead.value)\"\n [color]=\"ratingThemeColor(lead)\"\n [style.cssText]=\"ratingIconStyle(lead)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n [innerHTML]=\"lead.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"lead\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"lead\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"lead\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n }\n }\n }\n </div>\n\n @if (trailing(it); as tr) {\n @if (\n (config.templating?.statusPosition || \"inline\") ===\n \"top-right\" &&\n (tr?.type === \"chip\" || tr?.type === \"icon\")\n ) {\n <div class=\"tile-status\">\n @if (simpleRichContentNodes(tr); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @if (tr?.type === \"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(tr.color, tr.variant)\"\n >{{ tr.value }}</mat-chip\n >\n }\n @if (tr?.type === \"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n }\n </div>\n }\n }\n\n <div class=\"tile-body\">\n <div\n class=\"primary\"\n [ngClass]=\"primary(it)?.class\"\n [style.cssText]=\"primary(it)?.style\"\n >\n @if (simpleRichContentNodes(primary(it)); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (primary(it)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"primary(it)\"\n ></praxis-list-metric>\n } @else if (primary(it)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"primary(it)\"\n ></praxis-list-compose>\n } @else if (primary(it)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"primary(it)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ primary(it)?.value }}\n }\n </div>\n @if (layoutLines > 1) {\n <div\n class=\"secondary\"\n [ngClass]=\"secondary(it)?.class\"\n [style.cssText]=\"secondary(it)?.style\"\n >\n @if (simpleRichContentNodes(secondary(it)); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (secondary(it)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"secondary(it)\"\n ></praxis-list-metric>\n } @else if (secondary(it)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"secondary(it)\"\n ></praxis-list-compose>\n } @else if (secondary(it)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"secondary(it)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ secondary(it)?.value }}\n }\n </div>\n }\n\n @if (meta(it); as m) {\n <div\n class=\"tile-meta\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (config.templating?.metaPrefixIcon; as mpi) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n @if (\n simpleRichContentNodes(m, {\n slot: \"meta\",\n imageAlt: m.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(m.color) ? m.color : undefined\n \"\n [ngClass]=\"\n (m.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(m.color, m.variant)\"\n >{{ m.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(m);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, m.value)\"\n [color]=\"ratingThemeColor(m)\"\n [style.cssText]=\"ratingIconStyle(m)\"\n ></mat-icon>\n }\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"m.value\"\n [color]=\"\n isThemeColor(m.color) ? m.color : undefined\n \"\n [style.cssText]=\"iconStyle(m.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img [src]=\"m.value\" [alt]=\"m.imageAlt || ''\"\n /></span>\n }\n @case (\"html\") {\n <span [innerHTML]=\"m.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"m\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"m\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"m\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>{{ m.value }}</span>\n }\n }\n }\n </div>\n }\n\n @if (featuresVisible()) {\n <div class=\"tiles-features\">\n @for (\n f of config.templating?.features || [];\n track $index;\n let fi = $index\n ) {\n <span\n class=\"feature\"\n [ngClass]=\"featureSemanticClass(it, f.expr, f.class)\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n featureContent;\n context: { $implicit: it, feature: f }\n \"\n ></ng-container>\n </span>\n }\n </div>\n }\n\n @if (trailing(it); as tr2) {\n @if (\n !(\n (config.templating?.statusPosition || \"inline\") ===\n \"top-right\" &&\n (tr2?.type === \"chip\" || tr2?.type === \"icon\")\n )\n ) {\n <div\n class=\"tile-trailing\"\n [ngClass]=\"tr2.class\"\n [style.cssText]=\"tr2.style\"\n >\n @if (\n simpleRichContentNodes(tr2, {\n imageAlt: tr2.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (tr2.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr2.color) ? tr2.color : undefined\n \"\n [ngClass]=\"\n (tr2.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(tr2.color, tr2.variant)\n \"\n >{{ tr2.value }}</mat-chip\n >\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"tr2.value\"\n [color]=\"\n isThemeColor(tr2.color) ? tr2.color : undefined\n \"\n [style.cssText]=\"iconStyle(tr2.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img\n [src]=\"tr2.value\"\n [alt]=\"tr2.imageAlt || ''\"\n /></span>\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(tr2);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, tr2.value)\"\n [color]=\"ratingThemeColor(tr2)\"\n [style.cssText]=\"ratingIconStyle(tr2)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n <span [innerHTML]=\"tr2.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"tr2\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"tr2\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"tr2\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>{{ tr2.value }}</span>\n }\n }\n }\n </div>\n }\n }\n </div>\n </div>\n\n @if ((visibleActions(it, \"trailing\") || []).length) {\n <div\n class=\"tile-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(it, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, it, i)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'stroked')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'raised')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant || action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'flat')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n </div>\n }\n </div>\n </ng-template>\n\n <!-- Remote pagination stays attached to the collection footer. -->\n @if (isRemoteDataMode() && !(error$ | async)) {\n @if (page$ | async; as ps) {\n @if (total$ | async; as total) {\n @if (config.ui?.showRange === false) {\n <div class=\"list-paginator-total\" aria-live=\"polite\">\n {{ remoteTotalLabel(total) }}\n </div>\n }\n <mat-paginator\n [length]=\"total || 0\"\n [pageIndex]=\"ps.pageNumber || 0\"\n [pageSize]=\"ps.pageSize || config.layout?.pageSize || 10\"\n [pageSizeOptions]=\"listPageSizeOptions()\"\n [selectConfig]=\"paginatorSelectConfig\"\n [showFirstLastButtons]=\"true\"\n [class.hide-range]=\"config.ui?.showRange === false\"\n (page)=\"onPageChange($event)\"\n >\n </mat-paginator>\n }\n }\n }\n </ng-template>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block}.praxis-list-root{container-type:inline-size;--p-list-radius: var(--md-sys-shape-corner-medium, 16px);--p-list-shadow: var(--md-sys-elevation-level2);--p-list-border: 1px solid var(--md-sys-color-outline-variant);--p-list-blur: 10px;--p-list-surface: var(--md-sys-color-surface-container);--p-list-surface-low: var(--md-sys-color-surface);--p-list-surface-high: var( --md-sys-color-surface-container-high, var(--md-sys-color-surface-container) );--p-list-foreground: var(--md-sys-color-on-surface);--p-list-foreground-muted: var(--md-sys-color-on-surface-variant);--p-list-accent: var(--md-sys-color-primary);--p-list-accent-weak: var(--md-sys-color-primary-container);--p-list-item-surface: var( --md-sys-color-surface-container-low, var(--md-sys-color-surface) );--p-list-item-border: 1px solid var(--md-sys-color-outline-variant);--p-list-item-hover-surface: var(--md-sys-color-surface-container-low);--p-list-item-active-surface: var(--md-sys-color-surface-container);--p-list-item-selected-surface: var(--md-sys-color-surface-container);--p-list-grad-from: var(--md-sys-color-primary-container);--p-list-grad-to: var(--md-sys-color-tertiary-container);--p-list-grad-angle: 135deg;--p-list-grad-foreground: var( --md-sys-color-on-primary-container, var(--md-sys-color-on-surface) );--p-list-grad-foreground-muted: color-mix( in srgb, var(--p-list-grad-foreground) 76%, transparent );--p-list-leading-width: 36px;--p-list-trailing-min-width: 140px;--p-list-trailing-max-width: 320px;--p-list-item-gap: 10px;--p-list-item-padding-x: 14px;--p-list-item-padding-y: 10px;--p-list-item-stack-gap: 0px;--p-list-meta-size: .875rem;--p-list-meta-weight: 500;--p-list-chip-height: 22px;--p-list-chip-font-size: 12px;--p-list-trailing-padding-right: 8px;--p-list-action-button-height: 32px;--p-list-tile-minW: 240px;--p-list-tile-gap: 12px;--p-list-tile-padding: 12px;--p-list-tile-radius: 12px;--p-list-tile-media-radius: 12px;--p-list-tile-media-ratio: 1 / 1;--p-list-tile-hover-overlay: .04;--p-list-tile-press-overlay: .08;--p-list-tile-press-scale: .99}.list-assistant{display:flex;justify-content:flex-end;gap:8px;padding:4px 4px 8px}.action-loading{opacity:.65}.action-spinner{width:16px;height:16px;display:inline-flex;align-items:center;justify-content:center}.action-loading .mat-mdc-button-touch-target,.action-loading .mdc-button__label{opacity:.7}.skin-elevated,.skin-outline,.skin-flat,.skin-neumorphism{--p-list-item-surface: var( --mdc-elevated-card-container-color, var(--p-list-surface) );--p-list-item-border: var(--p-list-border)}.skin-elevated .item-card,.skin-outline .item-card,.skin-flat .item-card,.skin-neumorphism .item-card{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:var(--p-list-radius);box-shadow:var(--p-list-shadow);border:var(--p-list-border)}.skin-elevated .item-tile,.skin-outline .item-tile,.skin-flat .item-tile,.skin-neumorphism .item-tile{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));box-shadow:var(--p-list-shadow);border:var(--p-list-border)}.skin-elevated .mat-mdc-list-item .list-item-content,.skin-outline .mat-mdc-list-item .list-item-content,.skin-flat .mat-mdc-list-item .list-item-content,.skin-neumorphism .mat-mdc-list-item .list-item-content{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * .75);box-shadow:var(--p-list-shadow);border:var(--p-list-item-border);color:var(--p-list-foreground)}.skin-outline{--p-list-shadow: none;--p-list-border: 1px solid var(--md-sys-color-outline)}.skin-flat{--p-list-shadow: none;--p-list-border: 1px solid var(--md-sys-color-outline-variant)}.skin-neumorphism{--p-list-shadow: var(--md-sys-elevation-level2);--p-list-border: 1px solid var(--md-sys-color-outline-variant);--p-list-radius: 1.25rem}.skin-pill-soft .item-card,.skin-pill-soft .item-tile{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:var(--md-sys-elevation-level1);border:var(--p-list-border)}.skin-pill-soft .mat-mdc-list-item .list-item-content{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:var(--md-sys-elevation-level1);border:var(--p-list-border);color:var(--p-list-foreground)}.skin-gradient-tile{--p-list-foreground: var(--p-list-grad-foreground);--p-list-foreground-muted: var(--p-list-grad-foreground-muted);--p-list-item-hover-surface: linear-gradient( var(--p-list-grad-angle), var(--p-list-grad-from), var(--p-list-grad-to) );--p-list-item-active-surface: linear-gradient( var(--p-list-grad-angle), var(--p-list-grad-from), var(--p-list-grad-to) );--p-list-item-selected-surface: linear-gradient( var(--p-list-grad-angle), var(--p-list-grad-from), var(--p-list-grad-to) )}.skin-gradient-tile .item-card,.skin-gradient-tile .item-tile{background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));border-radius:var(--p-list-radius);color:var(--p-list-foreground)}.skin-gradient-tile .tile-media{background:color-mix(in srgb,var(--md-sys-color-surface) 74%,transparent);border-color:color-mix(in srgb,var(--p-list-foreground) 22%,transparent)}.skin-gradient-tile .tile-media mat-icon,.skin-gradient-tile .tile-meta,.skin-gradient-tile .tile-trailing,.skin-gradient-tile .feature,.skin-gradient-tile .secondary,.skin-gradient-tile .meta{color:var(--p-list-foreground-muted)}.skin-gradient-tile .tile-status,.skin-gradient-tile [data-rich-node-type=badge] .prx-rich-badge{color:var(--md-sys-color-on-secondary-container, var(--p-list-foreground));background:var(--md-sys-color-secondary-container, rgba(255, 255, 255, .72))}.skin-gradient-tile .mat-mdc-list-item .list-item-content{background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));border-radius:var(--p-list-radius);color:var(--p-list-foreground)}.skin-glass .item-card,.skin-glass .item-tile{background:var(--md-sys-color-surface-container-low);border-radius:var(--p-list-radius);border:1px solid var(--md-sys-color-outline-variant);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur))}.skin-glass .mat-mdc-list-item .list-item-content{background:var(--md-sys-color-surface-container-low);border-radius:var(--p-list-radius);border:1px solid var(--md-sys-color-outline-variant);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur));color:var(--p-list-foreground)}.density-compact .mat-mdc-list-item,.density-compact .item-card{--p-list-item-padding-y: 6px;--p-list-item-padding-x: 10px;--p-list-item-gap: 8px}.density-comfortable .mat-mdc-list-item,.density-comfortable .item-card{--p-list-item-padding-y: 8px;--p-list-item-padding-x: 12px;--p-list-item-gap: 10px}.praxis-list-root.lines-3 .mat-mdc-list-item{min-height:68px}.density-compact{--p-list-tile-gap: 10px;--p-list-tile-padding: 10px;--p-list-tile-minW: 188px}.density-comfortable{--p-list-tile-gap: 12px;--p-list-tile-padding: 12px}.item-spacing-none{--p-list-item-stack-gap: 0px}.item-spacing-tight{--p-list-item-stack-gap: 4px;--p-list-tile-gap: 10px}.item-spacing-default{--p-list-item-stack-gap: 6px;--p-list-tile-gap: 12px}.item-spacing-relaxed{--p-list-item-stack-gap: 14px;--p-list-tile-gap: 20px}.list-item-content{box-sizing:border-box;display:grid;grid-template-columns:var(--p-list-leading-width) minmax(0,1fr) minmax(var(--p-list-trailing-min-width),min(34vw,var(--p-list-trailing-max-width)));align-items:center;gap:var(--p-list-item-gap);padding:var(--p-list-item-padding-y) var(--p-list-item-padding-x);width:100%;min-width:0;border-radius:calc(var(--p-list-radius) * .75);overflow:visible}.list-item-content--row-layout{--p-list-row-template-columns: var(--p-list-leading-width) minmax(0, 1fr) minmax(var(--p-list-trailing-min-width), max-content);--p-list-row-gap: var(--p-list-item-gap);--p-list-row-align-items: center;grid-template-columns:var(--p-list-row-template-columns);gap:var(--p-list-row-gap);align-items:var(--p-list-row-align-items);position:relative}.list-row-main-action{position:absolute;inset:0;z-index:1;padding:0;border:0;border-radius:inherit;background:transparent;cursor:pointer}.list-row-main-action:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.list-row-slot{position:relative;z-index:0;min-width:0;height:100%;align-self:stretch;display:flex;align-items:var(--p-list-row-slot-align-items, center);gap:8px}.list-item-content--row-layout :is(.list-row-slot--actions,.list-row-slot--expand,.list-row-slot--trailing){z-index:2}.list-item-content--row-layout .list-row-slot>praxis-rich-content,.list-item-content--row-layout .list-row-slot>praxis-list-metric,.list-item-content--row-layout .list-row-slot>praxis-list-compose,.list-item-content--row-layout .list-row-slot>praxis-list-runtime-component{min-width:0}.list-row-slot--leading{justify-content:center}.list-row-slot--primary,.list-row-slot--secondary{min-width:0}.list-row-slot--meta{color:var(--p-list-foreground);font-variant-numeric:tabular-nums}.list-row-slot--trailing{flex-wrap:wrap;justify-content:flex-end}.list-row-slot .primary,.list-row-slot .secondary,.list-row-slot .meta,.list-row-slot .trailing{min-width:0}.list-item-main-action{grid-column:1/3;display:grid;grid-template-columns:var(--p-list-leading-width) minmax(0,1fr);align-items:center;gap:var(--p-list-item-gap);min-width:0;padding:0;border:0;background:transparent;color:inherit;text-align:start;font:inherit;cursor:pointer}.list-item-main-action:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px;border-radius:12px}.list-item-leading{display:flex;align-items:center;justify-content:center;min-height:100%}.leading-placeholder{width:24px;height:24px;display:inline-block}.list-item-text{min-width:0;display:grid;align-content:center;gap:2px}.list-item-trailing{box-sizing:border-box;min-width:0;width:100%;max-width:var(--p-list-trailing-max-width);display:grid;align-self:center;align-items:center;justify-content:end;justify-items:end;align-content:center;gap:4px;text-align:end;padding-right:var(--p-list-trailing-padding-right)}.list-item-trailing>*{align-self:center}.list-item-content--row-layout .list-item-actions{justify-content:flex-end}.list-item-actions{width:max-content;justify-self:end;align-self:center;display:flex;align-items:center;justify-content:flex-end;gap:4px;flex-wrap:wrap;max-width:100%;min-height:var(--p-list-action-button-height)}.list-item-actions .mat-mdc-button-base{min-height:var(--p-list-action-button-height);height:var(--p-list-action-button-height);display:inline-flex;align-items:center}.expand-toggle{color:var(--p-list-foreground-muted)}.mat-mdc-list-item .list-item-content{background:var(--p-list-item-surface);border:var(--p-list-item-border);transition:background .15s ease,box-shadow .15s ease,transform .15s ease}.mat-mdc-list-item{height:auto;align-items:stretch;overflow:visible}.mat-mdc-list-item.mdc-list-item--with-one-line,.mat-mdc-list-item.mdc-list-item--with-two-lines,.mat-mdc-list-item.mdc-list-item--with-three-lines,.mat-mdc-list-item-single-line,.mat-mdc-list-item-two-line,.mat-mdc-list-item-three-line{height:auto!important}.mdc-list-item{overflow:visible}.density-compact .mat-mdc-list-item{min-height:40px}.density-comfortable .mat-mdc-list-item{min-height:46px}.praxis-list-root:not(.density-compact):not(.density-comfortable) .mat-mdc-list-item{min-height:48px}.mat-mdc-list-item .mdc-list-item__content,.mat-mdc-list-item .mdc-list-item__primary-text{display:block!important;width:100%!important;height:auto!important;padding:0!important;margin:0!important;overflow:visible!important;box-sizing:border-box}.mat-mdc-list-item{padding:0!important;box-sizing:border-box}.praxis-list-root mat-list>mat-list-item:not(:last-child){margin-bottom:var(--p-list-item-stack-gap)}.mat-mdc-list-item:hover .list-item-content{background:var(--p-list-item-hover-surface)}.mat-mdc-list-item:active .list-item-content{background:var(--p-list-item-active-surface)}.mat-mdc-list-item:focus-visible .list-item-content{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.list-row-shell{width:100%;min-width:0}.list-row-shell--selectable{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:stretch;border-radius:calc(var(--p-list-radius) * .75);background:var(--p-list-item-surface);border:var(--p-list-item-border);color:var(--p-list-foreground);overflow:hidden;transition:background .15s ease,box-shadow .15s ease,border-color .15s ease}.list-selection-control{position:relative;z-index:2;display:flex;align-items:flex-start;justify-content:center;padding:var(--p-list-item-padding-y) var(--p-list-item-padding-x) var(--p-list-item-padding-y) 0}.praxis-list-root .mat-mdc-list-item .list-row-shell--selectable .list-item-content{background:transparent;border:0;border-radius:0;box-shadow:none}.mat-mdc-list-item:hover .list-row-shell--selectable{background:var(--p-list-item-hover-surface)}.mat-mdc-list-item:active .list-row-shell--selectable{background:var(--p-list-item-active-surface)}.list-row-shell--selected{background:var(--p-list-item-selected-surface);border-color:var(--md-sys-color-outline-variant)}.skin-elevated .list-row-shell--selectable,.skin-outline .list-row-shell--selectable,.skin-flat .list-row-shell--selectable,.skin-neumorphism .list-row-shell--selectable{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border:var(--p-list-item-border);box-shadow:var(--p-list-shadow)}.skin-pill-soft .list-row-shell--selectable{border-radius:calc(var(--p-list-radius) * 1.3);background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border:var(--p-list-border);box-shadow:var(--md-sys-elevation-level1)}.skin-gradient-tile .list-row-shell--selectable{border-radius:var(--p-list-radius);background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));color:var(--p-list-foreground)}.skin-glass .list-row-shell--selectable{border-radius:var(--p-list-radius);background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur))}.executive-inline-skin{--p-list-leading-width: 56px;--p-list-trailing-min-width: 232px;--p-list-item-padding-x: 14px;--p-list-item-padding-y: 8px;--p-list-item-gap: 10px;--p-list-surface: #f8fafb;--p-list-surface-low: #ffffff;--p-list-item-surface: #ffffff;--p-list-item-hover-surface: #f7fafc;--p-list-item-active-surface: #eef4f8;--p-list-item-selected-surface: #eef4f8;--p-list-foreground: #17324d;--p-list-foreground-muted: #72849a}.executive-inline-skin .list-item-content--row-layout.exec-row-layout{min-height:72px;padding:12px 20px;background:linear-gradient(180deg,#fff,#fbfcfd)!important;border:1px solid #dce5ec!important;border-radius:18px;box-shadow:0 1px 2px #0f223a08,0 4px 14px #0f223a08!important;color:#17324d!important}.executive-inline-skin .mat-mdc-list-item:hover .list-item-content--row-layout.exec-row-layout{background:linear-gradient(180deg,#fff,#f7fafc)!important}.executive-inline-skin .list-item-content--row-layout.exec-row-layout+.item-expansion{margin-left:10px;margin-right:10px}.executive-inline-skin .list-item-content--row-layout.exec-row-layout .list-row-slot{min-height:34px}.executive-inline-skin .list-row-slot--leading{justify-content:center}.executive-inline-skin .list-row-slot--identity{align-items:center;padding-right:4px}.executive-inline-skin .list-row-slot--balance,.executive-inline-skin .list-row-slot--limit,.executive-inline-skin .list-row-slot--risk,.executive-inline-skin .list-row-slot--alerts{justify-content:center;padding-inline:2px}.executive-inline-skin .list-row-slot--owner{justify-content:flex-start;padding-left:0}.executive-inline-skin .list-row-slot--actions{justify-content:flex-end;padding-left:0}.executive-inline-skin .list-row-slot--expand{justify-content:center}.executive-inline-skin .docs-expansion-leading{width:44px;height:44px;display:inline-flex;align-items:center;justify-content:center;border-radius:15px;background:linear-gradient(180deg,#edf4f9,#e6eef5);color:#1b5f86;font-size:1rem;font-weight:800;letter-spacing:.02em;box-shadow:0 1px 3px #1b5f861f;border:1px solid #dbe7f0}.executive-inline-skin .exec-balance-metric,.executive-inline-skin .exec-limit-metric,.executive-inline-skin .exec-risk-metric{min-width:0}.executive-inline-skin .exec-balance-metric .p-list-metric,.executive-inline-skin .exec-limit-metric .p-list-metric,.executive-inline-skin .exec-risk-metric .p-list-metric{gap:5px}.executive-inline-skin .exec-balance-metric .p-list-metric__label,.executive-inline-skin .exec-limit-metric .p-list-metric__label,.executive-inline-skin .exec-risk-metric .p-list-metric__label{font-size:.62rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#8090a1}.executive-inline-skin .exec-balance-metric .p-list-metric__value,.executive-inline-skin .exec-limit-metric .p-list-metric__value{font-size:.96rem;font-weight:800;letter-spacing:-.02em;color:#0d1f34;line-height:1.05}.executive-inline-skin .exec-limit-metric .p-list-metric__caption{font-size:.66rem;font-weight:700;color:#8090a1}.executive-inline-skin .exec-limit-metric .p-list-metric__progress{width:68px;justify-self:center;height:3px}.executive-inline-skin .exec-risk-metric{min-width:84px}.executive-inline-skin .exec-risk-metric .p-list-metric{justify-items:center}.executive-inline-skin .exec-risk-metric .p-list-metric__main{gap:6px}.executive-inline-skin .exec-risk-metric .p-list-metric__value{font-size:1.02rem;font-weight:800;letter-spacing:-.024em;line-height:1}.executive-inline-skin .exec-risk-metric .p-list-metric__progress{width:56px;justify-self:center;height:3px}.executive-inline-skin .exec-risk-metric .p-list-metric__caption{font-size:.7rem;font-weight:700;line-height:1.1}.executive-inline-skin .exec-risk-metric .p-list-metric__caption--below-progress{justify-self:center;margin-top:-1px}.executive-inline-skin .exec-risk-metric .p-list-metric__icon mat-icon{width:15px;height:15px;font-size:15px}.executive-inline-skin .list-item-actions{gap:6px;flex-wrap:nowrap}.executive-inline-skin .list-row-slot--actions .list-item-actions{justify-content:flex-end}.executive-inline-skin .list-item-actions button,.executive-inline-skin .expand-toggle{width:30px;height:30px;min-width:30px;border-radius:12px;color:#6d7f93!important}.executive-inline-skin .list-item-actions button mat-icon,.executive-inline-skin .expand-toggle mat-icon{width:18px;height:18px;font-size:18px}.executive-inline-skin .item-expansion{position:relative;margin-top:-10px;padding:22px 26px;background:linear-gradient(180deg,#fcfdfe,#f5f8fb);border-color:#e4eaf0;border-radius:0 0 22px 22px;box-shadow:inset 0 1px #ffffffdb}.lead-image{position:relative;width:96px;height:72px;border-radius:12px;overflow:hidden}.lead-image img{width:100%;height:100%;object-fit:cover;display:block}.lead-image .lead-badge{position:absolute;left:8px;bottom:8px}.inline-image{position:relative;width:20px;height:20px;border-radius:4px;overflow:hidden;display:inline-block;vertical-align:middle}.inline-image img{width:100%;height:100%;object-fit:cover;display:block}.inline-image .inline-badge{position:absolute;left:2px;bottom:2px}.model-media .lead-image{width:136px;height:96px;border-radius:16px}.model-media .list-item-content{gap:16px}.model-media .item-card{--p-list-leading-width: 136px}.model-hotel .lead-image{width:160px;height:110px;border-radius:18px}.model-hotel .item-card{--p-list-leading-width: 160px}.primary,.secondary{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis}.primary{-webkit-line-clamp:1;font-weight:600;color:var(--p-list-foreground)}.secondary{-webkit-line-clamp:1;color:var(--p-list-foreground-muted)}.lines-3 .tertiary{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.item-tile .primary{-webkit-line-clamp:2;font-size:.95rem;line-height:1.25rem}.item-tile .secondary{-webkit-line-clamp:2;font-size:.8rem;line-height:1.1rem}.tertiary{color:var(--p-list-foreground-muted)}.features{display:flex;flex-wrap:wrap;gap:12px;margin-top:6px;color:inherit}.feature{display:inline-flex;align-items:center;gap:6px}.feature-line{display:block}.feature-progress{display:block;width:88px;height:4px;margin-top:6px;border-radius:999px;background:#dbe5eb;overflow:hidden}.feature-progress__value{display:block;height:100%;border-radius:inherit;background:#2f9b69}.meta{color:var(--p-list-foreground);font-size:var(--p-list-meta-size);font-weight:var(--p-list-meta-weight);font-variant-numeric:tabular-nums;max-width:100%;min-width:0;display:inline-flex;align-items:center;justify-content:flex-end;gap:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.meta>span,.meta praxis-rich-content{min-width:0;display:inline-flex;align-items:center;justify-content:flex-end;gap:4px;line-height:1.15;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.meta .mat-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;line-height:1;vertical-align:middle}.trailing{color:var(--p-list-foreground-muted);margin-left:0;max-width:100%;min-width:0;display:inline-flex;justify-content:flex-end;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.trailing praxis-rich-content{min-width:0}:host ::ng-deep .meta praxis-rich-content .prx-rich-node[data-rich-node-type=badge],:host ::ng-deep .trailing praxis-rich-content .prx-rich-node[data-rich-node-type=badge],:host ::ng-deep .list-row-slot praxis-rich-content .prx-rich-node[data-rich-node-type=badge]{background:transparent;color:inherit}:host ::ng-deep .meta praxis-rich-content .prx-rich-node[data-rich-node-type=badge] .prx-rich-badge,:host ::ng-deep .trailing praxis-rich-content .prx-rich-node[data-rich-node-type=badge] .prx-rich-badge,:host ::ng-deep .list-row-slot praxis-rich-content .prx-rich-node[data-rich-node-type=badge] .prx-rich-badge{min-height:22px;padding:1px 9px;border:1px solid var(--p-list-chip-border-color, transparent);background:var(--p-list-chip-background, var(--md-sys-color-primary-container, #e8def8));color:var(--p-list-chip-color, var(--md-sys-color-on-primary-container, #21005d));line-height:18px}.section-header{font-size:.85rem;color:var(--p-list-foreground-muted);padding:8px 12px}.section-header--setup{margin:8px 12px 12px;padding:14px 16px;border:1px dashed var(--md-sys-color-outline-variant);border-radius:8px;background:color-mix(in srgb,var(--p-list-surface) 92%,var(--md-sys-color-primary) 8%);color:var(--p-list-foreground);font-weight:500}.praxis-list-root mat-list{display:block;padding-bottom:12px}.praxis-list-root .mat-divider{margin-left:var(--p-list-item-padding-x);margin-right:var(--p-list-item-padding-x)}.item-expansion{margin:0 var(--p-list-item-padding-x) var(--p-list-item-stack-gap);padding:var(--p-list-expansion-padding, 14px 16px);background:var(--md-sys-color-surface-container-low, var(--p-list-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:calc(var(--p-list-radius) * .75)}.item-expansion--detached,.item-expansion--modal{margin-top:var(--p-list-item-stack-gap);background:var(--md-sys-color-surface-container, var(--p-list-surface));box-shadow:var(--md-sys-elevation-level1)}.item-expansion--modal{border-color:var(--md-sys-color-outline);box-shadow:var(--md-sys-elevation-level3)}.item-expansion-grid{display:grid;grid-template-columns:var(--p-list-expansion-columns, repeat(auto-fit, minmax(220px, 1fr)));gap:var(--p-list-expansion-gap, 16px)}.expansion-section{min-width:0;display:grid;gap:10px;align-content:start}.expansion-section-title{font-size:.78rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--p-list-foreground-muted)}.expansion-info-list,.expansion-timeline,.expansion-key-value{display:grid;gap:10px}.expansion-info-item,.expansion-timeline-item{display:grid;gap:4px;padding:10px 12px;border-radius:14px;background:var(--p-list-item-surface);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 72%,transparent 28%)}.expansion-info-title,.expansion-timeline-title{font-size:.92rem;font-weight:600;color:var(--p-list-foreground)}.expansion-info-value,.expansion-timeline-meta,.expansion-timeline-description,.expansion-empty{font-size:.82rem;line-height:1.45;color:var(--p-list-foreground-muted)}.expansion-chip-list{display:flex;flex-wrap:wrap;gap:8px}.expansion-chip{background:var(--p-list-item-surface);border:1px solid var(--md-sys-color-outline-variant)}.expansion-key-value{margin:0}.expansion-key-value-row{display:grid;gap:4px;padding:4px 0 10px;border-bottom:1px solid var(--md-sys-color-outline-variant)}.expansion-key-value-row dt,.expansion-key-value-row dd{margin:0}.expansion-key-value-row dt{font-size:.78rem;font-weight:600;color:var(--p-list-foreground-muted)}.expansion-key-value-row dd{font-size:.9rem;color:var(--p-list-foreground)}.cards-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,max(340px,var(--p-list-tile-minW))),1fr));gap:max(var(--p-list-tile-gap),16px)}.tiles-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(var(--p-list-tile-minW),1fr));gap:var(--p-list-tile-gap)}.item-tile{position:relative;display:flex;flex-direction:column;gap:10px;padding:var(--p-list-tile-padding);border-radius:var(--p-list-tile-radius);background:var(--p-list-surface);border:var(--p-list-border);color:var(--p-list-foreground);cursor:pointer;min-height:160px;transition:box-shadow .16s ease-out,border-color .16s ease-out,transform .12s ease-out,background .16s ease-out}.item-tile:hover{box-shadow:var(--md-sys-elevation-level2);border-color:var(--md-sys-color-outline-variant);background:var(--p-list-item-hover-surface)}.item-tile:active{transform:scale(var(--p-list-tile-press-scale));background:var(--p-list-item-active-surface)}.tile-main-action{display:flex;flex:1;min-width:0;flex-direction:column;gap:10px;color:inherit;cursor:pointer}.tile-main-action:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px;border-radius:calc(var(--p-list-tile-radius) - 4px)}.tile-media{width:100%;aspect-ratio:var(--p-list-tile-media-ratio);border-radius:var(--p-list-tile-media-radius);background:var(--p-list-item-surface);border:1px solid var(--md-sys-color-outline-variant);display:grid;place-items:center;overflow:hidden}.tile-media img{width:100%;height:100%;object-fit:cover;display:block}.tile-media mat-icon{font-size:28px;height:28px;width:28px;color:var(--p-list-foreground-muted)}.tile-body{display:grid;gap:6px;min-width:0}.tile-meta{display:inline-flex;align-items:center;gap:6px;font-size:.75rem;color:var(--p-list-foreground-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tile-trailing{font-size:.75rem;color:var(--p-list-foreground-muted);white-space:nowrap}.tiles-features{display:flex;flex-wrap:wrap;gap:8px;color:inherit}.tile-status{position:absolute;top:10px;right:10px}.tile-actions{position:absolute;top:8px;left:8px;display:flex;gap:4px;opacity:0;pointer-events:none;transform:translateY(-2px);transition:opacity .16s ease-out,transform .16s ease-out}.item-tile:hover .tile-actions,.item-tile:focus-within .tile-actions{opacity:1;pointer-events:auto;transform:translateY(0)}@media(hover:none){.tile-actions{opacity:1;pointer-events:auto;transform:none}}@media(max-width:600px){.tiles-grid{grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}}@media(prefers-reduced-motion:reduce){.item-tile,.tile-actions{transition:none}.item-tile:active{transform:none}.item-card{transition:none}.item-card:active{transform:none}}.item-card{padding:max(var(--p-list-tile-padding),16px);display:flex;flex-direction:column;gap:var(--p-list-item-gap);min-height:220px;position:relative;cursor:pointer;background:var(--p-list-surface);border:var(--p-list-border);border-radius:var(--p-list-tile-radius);box-shadow:var(--p-list-shadow);transition:box-shadow .16s ease-out,border-color .16s ease-out,transform .12s ease-out,background .16s ease-out;--p-list-leading-width: 96px;--p-list-trailing-min-width: 0px}.card-main-action{display:block;width:100%;min-width:0;padding:0;border:0;background:transparent;color:inherit;text-align:start;font:inherit;cursor:pointer}.card-main-action:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px;border-radius:calc(var(--p-list-tile-radius) - 4px)}.item-card .list-item-content{display:block;padding:0;min-width:0;overflow:hidden}.item-card .list-item-leading{position:absolute;top:18px;left:18px;min-height:0;z-index:1}.item-card .leading-placeholder{width:36px;height:36px}.item-card .leading-icon.mat-icon,.item-card .list-item-leading>mat-icon{width:36px;height:36px;line-height:36px;font-size:20px;display:inline-flex;align-items:center;justify-content:center;border-radius:14px;background:linear-gradient(145deg,color-mix(in srgb,var(--md-sys-color-primary) 18%,transparent),color-mix(in srgb,var(--md-sys-color-primary-container) 34%,transparent));color:var(--md-sys-color-primary)}.item-card .list-item-text{display:grid;gap:9px;padding-left:50px;width:100%;min-width:0;box-sizing:border-box}.item-card .primary{padding-right:min(50%,160px);font-size:1rem;font-weight:800;letter-spacing:-.01em;line-height:1.18;min-width:0;overflow-wrap:anywhere}.item-card .secondary{color:var(--p-list-foreground-muted);font-weight:600;line-height:1.35;min-width:0;overflow-wrap:anywhere}.item-card .features{display:flex;flex-direction:column;gap:8px;margin-top:2px}.item-card .feature{display:inline-flex;align-items:start;gap:8px;min-width:0;max-width:100%;overflow-wrap:anywhere}.item-card .feature mat-icon{margin-top:1px;color:var(--p-list-foreground-muted);opacity:.86}.item-card .list-item-trailing{min-width:var(--p-list-trailing-min-width);grid-column:1/-1;align-items:stretch;padding-right:0}.card-detail-strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(148px,1fr));gap:10px;margin-top:16px}.card-detail{min-width:0;padding:11px 12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent);border-radius:14px;background:linear-gradient(145deg,color-mix(in srgb,var(--md-sys-color-primary-container) 22%,transparent),color-mix(in srgb,var(--p-list-surface) 92%,transparent));box-shadow:inset 0 1px #ffffff14}.card-detail__label{display:block;margin-bottom:3px;color:var(--p-list-foreground-muted);font-size:.68rem;font-weight:700;letter-spacing:.08em;line-height:1.1;text-transform:uppercase}.card-detail__value{display:block;min-width:0;color:var(--p-list-foreground);font-size:.94rem;font-weight:800;line-height:1.2;overflow-wrap:normal;word-break:normal}.card-detail__value,.card-detail__value *{white-space:nowrap}.card-detail--balance .card-detail__value{color:var(--md-sys-color-primary)}.card-detail--limit .card-detail__value{color:var(--md-sys-color-error)}.card-detail--risk .card-detail__value{color:var(--md-sys-color-tertiary, var(--p-list-foreground))}.item-card:hover{box-shadow:var(--md-sys-elevation-level3);border-color:var(--md-sys-color-outline-variant);background:var(--p-list-item-hover-surface)}.item-card:active{transform:scale(var(--p-list-tile-press-scale));background:var(--p-list-item-active-surface)}.card-actions{display:flex;align-items:center;justify-content:flex-end;gap:4px;padding:6px 8px 4px;border-top:1px solid var(--md-sys-color-outline-variant);margin-top:auto}.status-overlay{position:absolute;top:14px;right:14px;max-width:min(42%,148px);overflow:hidden;pointer-events:none}.status-overlay .mat-mdc-chip{pointer-events:auto;width:100%;min-width:0;max-width:100%}.status-overlay .mdc-evolution-chip__text-label,.status-overlay .mat-mdc-chip-action-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.leading-icon.mat-icon{width:36px;height:36px;line-height:36px;font-size:20px;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.mat-mdc-chip{--mdc-chip-container-height: var(--p-list-chip-height);font-size:var(--p-list-chip-font-size)}.meta .mat-icon{font-size:18px;height:18px;width:18px}.features{overflow:hidden}@keyframes shimmer{0%{background-position:-468px 0}to{background-position:468px 0}}.skeleton{animation-duration:1.2s;animation-fill-mode:forwards;animation-iteration-count:infinite;animation-name:shimmer;animation-timing-function:linear;background:linear-gradient(to right,var(--md-sys-color-surface-container-low) 8%,var(--md-sys-color-surface-container) 18%,var(--md-sys-color-surface-container-low) 33%);background-size:800px 104px;position:relative}.skeleton-avatar{width:32px;height:32px;border-radius:50%}.skeleton-line{height:10px;margin:6px 0;border-radius:6px}.skeleton-chip{width:48px;height:18px;border-radius:999px}.chip-outlined.mat-mdc-chip{background:transparent!important;border:1px solid currentColor}.w-60{width:60%}.w-40{width:40%}.list-collection-toolbar{display:flex;align-items:center;gap:12px;padding:12px;margin-bottom:8px;color:var(--p-list-foreground);background:var(--p-list-surface-low);border:var(--p-list-border);border-radius:calc(var(--p-list-radius) * .75)}.list-toolbar-search{flex:1 1 320px;min-width:220px;max-width:440px}.list-toolbar-commands{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:8px;margin-inline-start:auto}.list-toolbar-commands button{border-radius:8px}.list-toolbar-commands button .mat-icon{margin-right:6px}.list-toolbar-sort{width:200px}.list-remote-error{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:12px;margin:8px 4px 0;padding:16px;color:var(--md-sys-color-on-error-container);background:var(--md-sys-color-error-container);border-radius:calc(var(--p-list-radius) * .75)}.list-remote-error__copy{display:flex;flex-direction:column;gap:4px;min-width:0}.list-remote-error__copy span{color:color-mix(in srgb,var(--md-sys-color-on-error-container) 82%,transparent);line-height:1.45}.list-remote-error button{color:var(--md-sys-color-on-error-container);--mat-button-outlined-label-text-color: var( --md-sys-color-on-error-container );--mat-button-outlined-outline-color: color-mix( in srgb, var(--md-sys-color-on-error-container) 42%, transparent );--mdc-outlined-button-label-text-color: var( --md-sys-color-on-error-container );--mdc-outlined-button-outline-color: color-mix( in srgb, var(--md-sys-color-on-error-container) 42%, transparent )}.mat-mdc-paginator{margin-top:14px;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--p-list-surface-low);color:var(--p-list-foreground)}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-container{min-height:40px;padding:4px 8px;gap:6px}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-page-size-select{margin-left:8px;width:92px}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-page-size-select .mat-mdc-text-field-wrapper{--mdc-outlined-text-field-container-color: color-mix( in srgb, var(--p-list-surface-low) 92%, var(--md-sys-color-on-surface) 8% );--mdc-outlined-text-field-input-text-color: var(--p-list-foreground);min-height:40px;padding-inline:12px 8px;background:color-mix(in srgb,var(--p-list-surface-low) 92%,var(--md-sys-color-on-surface) 8%)!important;background-color:color-mix(in srgb,var(--p-list-surface-low) 92%,var(--md-sys-color-on-surface) 8%)!important;color:var(--p-list-foreground)!important}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-page-size-select .mat-mdc-form-field-infix{min-height:40px;padding-block:8px;width:auto}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-page-size-select .mat-mdc-select-trigger{min-height:24px;padding-right:2px;color:var(--p-list-foreground)}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-range-label,:host ::ng-deep .mat-mdc-paginator .mat-mdc-select-value-text{font-size:12px;color:var(--p-list-foreground-muted)}.list-paginator-total{margin-top:14px;padding:4px 8px 0;font-size:12px;color:var(--p-list-foreground-muted)}:host ::ng-deep .mat-mdc-paginator.hide-range .mat-mdc-paginator-range-label{display:none}::ng-deep .praxis-list-paginator-select-panel.mat-mdc-select-panel{--mat-select-panel-background-color: var( --md-sys-color-surface-container, #fff );background:var(--md-sys-color-surface-container, #fff)!important;background-color:var(--md-sys-color-surface-container, #fff)!important;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #d7dbe5) 70%,transparent);box-shadow:0 12px 28px color-mix(in srgb,var(--md-sys-color-shadow, #000) 28%,transparent)}::ng-deep .praxis-list-paginator-select-panel .mat-mdc-option{--mat-option-label-text-color: var(--md-sys-color-on-surface, #111827);color:var(--md-sys-color-on-surface, #111827)!important}::ng-deep .praxis-list-paginator-select-panel .mat-mdc-option:hover:not(.mdc-list-item--disabled),::ng-deep .praxis-list-paginator-select-panel .mat-mdc-option.mat-mdc-option-active{--mat-option-hover-state-layer-color: color-mix( in srgb, var(--md-sys-color-primary, #2f7cff) 14%, transparent );background:color-mix(in srgb,var(--md-sys-color-primary, #2f7cff) 14%,transparent)!important;color:var(--md-sys-color-on-surface, #111827)!important}.muted{color:var(--p-list-foreground-muted)}@container (max-width: 720px){.list-item-content{grid-template-columns:var(--p-list-leading-width) minmax(0,1fr)}.list-item-content--row-layout{grid-template-columns:var(--p-list-leading-width) minmax(0,1fr) max-content;grid-auto-flow:row;align-items:start}.list-item-content--row-layout .list-row-slot--leading{grid-column:1;grid-row:1}.list-item-content--row-layout .list-row-slot--primary,.list-item-content--row-layout .list-row-slot--identity{grid-column:2/-1;grid-row:1}.list-item-content--row-layout .list-row-slot--secondary{grid-column:2/-1;grid-row:2}.list-item-content--row-layout :is(.list-row-slot--primary,.list-row-slot--secondary){white-space:normal;overflow-wrap:anywhere}.list-item-content--row-layout .list-row-slot--meta{grid-column:2;grid-row:3;justify-content:flex-start}.list-item-content--row-layout .list-row-slot--trailing{grid-column:3;grid-row:3;justify-content:flex-end}.list-selection-control{padding-left:0}.list-item-content--row-layout :is(.list-row-slot--balance,.list-row-slot--limit,.list-row-slot--risk,.list-row-slot--alerts,.list-row-slot--owner,.list-row-slot--actions,.list-row-slot--expand){grid-column:1/-1}.list-item-trailing{grid-column:1/-1;display:flex;flex-wrap:wrap;justify-content:flex-start;padding-right:0;align-items:center;text-align:start}.list-item-trailing .list-item-actions{margin-inline-start:auto}.item-expansion{margin-left:0;margin-right:0}.list-collection-toolbar{flex-direction:column;align-items:stretch}.list-toolbar-search,.list-toolbar-sort{flex:0 0 auto;width:100%;max-width:none;min-width:0}.list-toolbar-commands{width:100%;margin-inline-start:0}.list-toolbar-sort{flex:1 1 100%}.list-toolbar-commands button{flex:0 0 auto}.list-remote-error{grid-template-columns:auto minmax(0,1fr)}.list-remote-error button{grid-column:1/-1;justify-self:stretch}}@media(max-width:720px){.list-item-content{grid-template-columns:var(--p-list-leading-width) minmax(0,1fr)}.list-item-content--row-layout{grid-template-columns:var(--p-list-leading-width) minmax(0,1fr) max-content;grid-auto-flow:row;align-items:start}.list-item-content--row-layout .list-row-slot--leading{grid-column:1;grid-row:1}.list-item-content--row-layout .list-row-slot--primary,.list-item-content--row-layout .list-row-slot--identity{grid-column:2/-1;grid-row:1}.list-item-content--row-layout .list-row-slot--secondary{grid-column:2/-1;grid-row:2}.list-item-content--row-layout :is(.list-row-slot--primary,.list-row-slot--secondary){white-space:normal;overflow-wrap:anywhere}.list-item-content--row-layout .list-row-slot--meta{grid-column:2;grid-row:3;justify-content:flex-start}.list-item-content--row-layout .list-row-slot--trailing{grid-column:3;grid-row:3;justify-content:flex-end}.list-selection-control{padding-left:0}.list-item-content--row-layout :is(.list-row-slot--balance,.list-row-slot--limit,.list-row-slot--risk,.list-row-slot--alerts,.list-row-slot--owner,.list-row-slot--actions,.list-row-slot--expand){grid-column:1/-1}.list-item-trailing{grid-column:1/-1;display:flex;flex-wrap:wrap;justify-content:flex-start;padding-right:0;align-items:center;text-align:start}.list-item-trailing .list-item-actions{margin-inline-start:auto}.item-expansion{margin-left:0;margin-right:0}.list-collection-toolbar{flex-direction:column;align-items:stretch}.list-toolbar-search,.list-toolbar-sort{flex:0 0 auto;width:100%;max-width:none;min-width:0}.list-toolbar-commands{width:100%;margin-inline-start:0}.list-toolbar-sort{flex:1 1 100%}.list-toolbar-commands button{flex:0 0 auto}.list-remote-error{grid-template-columns:auto minmax(0,1fr)}.list-remote-error button{grid-column:1/-1;justify-self:stretch}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i2.MatList, selector: "mat-list", exportAs: ["matList"] }, { kind: "component", type: i2.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "component", type: i2.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i3$3.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "component", type: i4$2.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: MatDividerModule }, { 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"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i5.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["role", "id", "aria-label", "aria-description", "value", "color", "removable", "highlighted", "disableRipple", "disabled"], outputs: ["removed", "destroyed"], exportAs: ["matChip"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i7.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: i7.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i5$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: i5$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i5$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: MatProgressSpinnerModule }, { kind: "component", type: i9.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i11$1.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.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: MatTooltipModule }, { kind: "directive", type: i12.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: PraxisAiAssistantShellComponent, selector: "praxis-ai-assistant-shell", inputs: ["labels", "mode", "state", "contextItems", "attachments", "messages", "quickReplies", "recommendedIntents", "prompt", "statusText", "errorText", "testIdPrefix", "panelTestId", "submitTestId", "applyTestId", "primaryAction", "secondaryActions", "governanceActions", "busy", "canSubmit", "canApply", "submitOnEnter", "showAttachAction", "enablePastedAttachments", "enableFileAttachments", "attachmentAccept", "attachmentMultiple", "voiceInputMode", "voiceLanguage", "draggable", "resizable", "minWidth", "minHeight", "margin", "layout"], outputs: ["promptChange", "submitPrompt", "apply", "retryTurn", "cancelTurn", "shellAction", "close", "attach", "attachmentsPasted", "attachmentsSelected", "removeAttachment", "messageAction", "editMessage", "resendMessage", "quickReply", "recommendedIntent", "layoutChange"] }, { kind: "component", type: ExecutiveBadgeComponent, selector: "praxis-executive-badge", inputs: ["ariaLabel", "label", "variant"] }, { kind: "component", type: ExecutiveAlertsComponent, selector: "praxis-executive-alerts", inputs: ["alerts"] }, { kind: "component", type: ExecutiveOwnerComponent, selector: "praxis-executive-owner", inputs: ["name"] }, { kind: "component", type: PraxisRichContent, selector: "praxis-rich-content", inputs: ["document", "nodes", "context", "hostCapabilities", "layout", "rootClassName"] }, { kind: "component", type: PraxisListMetricComponent, selector: "praxis-list-metric", inputs: ["metric"] }, { kind: "component", type: PraxisListComposeComponent, selector: "praxis-list-compose", inputs: ["compose"] }, { kind: "component", type: PraxisListRuntimeComponentComponent, selector: "praxis-list-runtime-component", inputs: ["component"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
15573
15648
|
}
|
|
15574
15649
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisList, decorators: [{
|
|
15575
15650
|
type: Component,
|
|
@@ -15577,6 +15652,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
15577
15652
|
CommonModule,
|
|
15578
15653
|
ReactiveFormsModule,
|
|
15579
15654
|
MatListModule,
|
|
15655
|
+
MatCheckboxModule,
|
|
15656
|
+
MatRadioModule,
|
|
15580
15657
|
MatDividerModule,
|
|
15581
15658
|
MatIconModule,
|
|
15582
15659
|
PraxisIconDirective,
|
|
@@ -15604,7 +15681,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
15604
15681
|
ListDataService,
|
|
15605
15682
|
MatPaginatorIntl,
|
|
15606
15683
|
providePraxisListI18n(),
|
|
15607
|
-
], template: "<div\n class=\"praxis-list-root\"\n [ngClass]=\"skinClasses\"\n [attr.data-skin-scope]=\"skinScopeId\"\n [attr.aria-label]=\"config.a11y?.ariaLabel || null\"\n [attr.aria-labelledby]=\"config.a11y?.ariaLabelledBy || null\"\n>\n @if (inlineCss) {\n <style [attr.nonce]=\"cspNonce || null\" [textContent]=\"inlineCss\"></style>\n }\n\n @if (enableCustomization) {\n <div class=\"list-assistant\">\n <button\n mat-mini-fab\n type=\"button\"\n color=\"primary\"\n (click)=\"openConfigEditor()\"\n [matTooltip]=\"configEditorLabel()\"\n [attr.aria-label]=\"configEditorLabel()\"\n data-testid=\"praxis-list-open-config-editor\"\n >\n <mat-icon [praxisIcon]=\"'edit'\"></mat-icon>\n </button>\n <button\n mat-mini-fab\n type=\"button\"\n color=\"primary\"\n (click)=\"openAiAssistant()\"\n matTooltip=\"Copiloto semantico Praxis\"\n aria-label=\"Abrir copiloto semantico Praxis da lista\"\n data-testid=\"praxis-list-ai-assistant-trigger\"\n >\n <mat-icon [praxisIcon]=\"'auto_awesome'\"></mat-icon>\n </button>\n </div>\n }\n\n @if (aiAssistantOpen && aiAssistantViewState) {\n <praxis-ai-assistant-shell\n [labels]=\"aiAssistantLabels\"\n [mode]=\"aiAssistantViewState.mode\"\n [state]=\"aiAssistantViewState.state\"\n [contextItems]=\"aiAssistantViewState.contextItems\"\n [attachments]=\"aiAssistantViewState.attachments\"\n [messages]=\"aiAssistantViewState.messages\"\n [quickReplies]=\"aiAssistantViewState.quickReplies\"\n [prompt]=\"aiAssistantPrompt\"\n [statusText]=\"aiAssistantViewState.statusText\"\n [errorText]=\"aiAssistantViewState.errorText\"\n [busy]=\"\n aiAssistantViewState.state === 'processing' ||\n aiAssistantViewState.state === 'applying'\n \"\n [canApply]=\"aiAssistantViewState.canApply\"\n [layout]=\"aiAssistantLayout\"\n testIdPrefix=\"praxis-list-ai-assistant\"\n panelTestId=\"praxis-list-ai-assistant-panel\"\n submitTestId=\"praxis-list-ai-assistant-submit\"\n applyTestId=\"praxis-list-ai-assistant-apply\"\n (promptChange)=\"onAiAssistantPromptChange($event)\"\n (submitPrompt)=\"onAiAssistantSubmit($event)\"\n (apply)=\"onAiAssistantApply()\"\n (retryTurn)=\"onAiAssistantRetry()\"\n (cancelTurn)=\"onAiAssistantCancel()\"\n (quickReply)=\"onAiAssistantQuickReply($event)\"\n (editMessage)=\"onAiAssistantEditMessage($event)\"\n (resendMessage)=\"onAiAssistantResendMessage($event)\"\n (layoutChange)=\"onAiAssistantLayoutChange($event)\"\n (close)=\"closeAiAssistant()\"\n ></praxis-ai-assistant-shell>\n }\n\n <!-- Skeleton while loading -->\n @if ((loading$ | async) && hasSkeleton()) {\n @if (isListVariant()) {\n <mat-list>\n @for (_ of skeletonItems(); track $index; let i = $index) {\n <mat-list-item>\n <div class=\"list-item-content\">\n <div class=\"list-item-leading\">\n <div class=\"skeleton skeleton-avatar\"></div>\n </div>\n <div class=\"list-item-text\">\n <div class=\"skeleton skeleton-line w-60\"></div>\n @if (layoutLines > 1) {\n <div class=\"skeleton skeleton-line w-40\"></div>\n }\n </div>\n <div class=\"list-item-trailing\">\n <div class=\"skeleton skeleton-chip\"></div>\n </div>\n </div>\n </mat-list-item>\n }\n </mat-list>\n } @else {\n <div class=\"cards-grid\">\n @for (_ of skeletonItems(); track $index; let i = $index) {\n <div class=\"item-card\">\n <div class=\"list-item-content\">\n <div class=\"list-item-leading\">\n <div class=\"skeleton skeleton-avatar\"></div>\n </div>\n <div class=\"list-item-text\">\n <div class=\"skeleton skeleton-line w-60\"></div>\n @if (layoutLines > 1) {\n <div class=\"skeleton skeleton-line w-40\"></div>\n }\n </div>\n <div class=\"list-item-trailing\">\n <div class=\"skeleton skeleton-chip\"></div>\n </div>\n </div>\n </div>\n }\n </div>\n }\n } @else {\n <ng-container *ngTemplateOutlet=\"notLoading\"></ng-container>\n }\n\n <ng-template #notLoading>\n @if (\n config.export?.enabled ||\n (isRemoteDataMode() &&\n (config.ui?.showSort ||\n (config.ui?.showSearch && config.ui?.searchField)))\n ) {\n <div\n class=\"list-collection-toolbar\"\n role=\"group\"\n [attr.aria-label]=\"t('toolbar.label', 'Controles da lista')\"\n >\n @if (\n isRemoteDataMode() && config.ui?.showSearch && config.ui?.searchField\n ) {\n <mat-form-field\n class=\"list-toolbar-search\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n >\n <mat-label>{{\n config.ui?.searchPlaceholder || t(\"toolbar.search\", \"Buscar\")\n }}</mat-label>\n <mat-icon matPrefix [praxisIcon]=\"'search'\"></mat-icon>\n <input\n matInput\n type=\"search\"\n (input)=\"onSearchInput($any($event.target).value)\"\n />\n </mat-form-field>\n }\n\n <div class=\"list-toolbar-commands\">\n @if (isRemoteDataMode() && config.ui?.showSort) {\n <mat-form-field\n class=\"list-toolbar-sort\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n >\n <mat-label>{{ t(\"toolbar.sort\", \"Ordenar por\") }}</mat-label>\n @let activeSort = activeSortValue((page$ | async)?.sort);\n <mat-select\n [value]=\"activeSort\"\n (selectionChange)=\"onSortChange($event.value)\"\n >\n @if (activeSort && !hasSortOption(activeSort)) {\n <mat-option [value]=\"activeSort\">{{ activeSort }}</mat-option>\n }\n @for (op of config.ui?.sortOptions || []; track $index) {\n <mat-option [value]=\"sortOptionValue(op)\">{{\n sortOptionLabel(op)\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n\n @if (config.export?.enabled) {\n <button\n mat-stroked-button\n type=\"button\"\n [matMenuTriggerFor]=\"listExportMenu\"\n [attr.aria-label]=\"t('export.button', 'Exportar')\"\n [disabled]=\"exportBusy\"\n [attr.aria-busy]=\"exportBusy\"\n >\n <mat-icon [praxisIcon]=\"'download'\"></mat-icon>\n {{ t(\"export.button\", \"Exportar\") }}\n </button>\n <span class=\"cdk-visually-hidden\" aria-live=\"polite\">\n {{ exportStatusMessage }}\n </span>\n <mat-menu #listExportMenu=\"matMenu\">\n @for (format of listExportFormats(); track format) {\n <button\n mat-menu-item\n type=\"button\"\n (click)=\"onExportAction(format)\"\n >\n <mat-icon [praxisIcon]=\"exportIcon(format)\"></mat-icon>\n {{ format.toUpperCase() }}\n </button>\n }\n </mat-menu>\n }\n </div>\n </div>\n }\n\n @if (error$ | async; as remoteError) {\n <div class=\"list-remote-error\" role=\"alert\">\n <mat-icon [praxisIcon]=\"'error_outline'\"></mat-icon>\n <div class=\"list-remote-error__copy\">\n <strong>{{\n t(\"remoteError.title\", \"N\u00E3o foi poss\u00EDvel carregar a lista\")\n }}</strong>\n <span>\n {{\n remoteError.filtered\n ? t(\n \"remoteError.filteredDescription\",\n \"Os filtros permanecem aplicados. Nenhum dado sem filtro foi exibido.\"\n )\n : t(\n \"remoteError.description\",\n \"Verifique a conex\u00E3o e tente novamente.\"\n )\n }}\n </span>\n </div>\n <button mat-stroked-button type=\"button\" (click)=\"retryRemoteData()\">\n {{ t(\"remoteError.retry\", \"Tentar novamente\") }}\n </button>\n </div>\n }\n\n <!-- Empty state -->\n @if (items$ | async; as all) {\n @if (all.length === 0 && !(error$ | async)) {\n <div\n class=\"section-header\"\n [class.section-header--setup]=\"isResourceConnectionMissing()\"\n >\n @if (emptyStateTemplate(); as empty) {\n @if (\n simpleRichContentNodes(empty, {\n imageAlt: config.templating?.emptyState?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content [nodes]=\"richNodes\"></praxis-rich-content>\n } @else {\n @switch (empty.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"empty.value\"\n [ngClass]=\"empty.class\"\n [color]=\"\n isThemeColor(empty.color) ? empty.color : undefined\n \"\n [style.cssText]=\"\n (empty.style ? empty.style + ';' : '') +\n iconStyle(empty.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <span\n class=\"inline-image\"\n [ngClass]=\"empty.class\"\n [style.cssText]=\"empty.style\"\n >\n <img\n [src]=\"empty.value\"\n [alt]=\"config.templating?.emptyState?.imageAlt || ''\"\n />\n @if (empty.badge?.value) {\n <mat-chip\n class=\"inline-badge\"\n [color]=\"\n isThemeColor(empty.badge?.color)\n ? empty.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (empty.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(empty.badge?.color, empty.badge?.variant)\n \"\n >{{ empty.badge?.value }}</mat-chip\n >\n }\n </span>\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(empty.color) ? empty.color : undefined\n \"\n [ngClass]=\"[\n empty.class || '',\n (empty.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : '',\n ]\"\n [style.cssText]=\"\n chipStyle(empty.color, empty.variant) +\n (empty.style ? ';' + empty.style : '')\n \"\n >{{ empty.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n <span [ngClass]=\"empty.class\" [style.cssText]=\"empty.style\">\n @for (\n _ of ratingRange(empty);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, empty.value)\"\n [color]=\"ratingThemeColor(empty)\"\n [style.cssText]=\"ratingIconStyle(empty)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"empty.class\"\n [style.cssText]=\"empty.style\"\n [innerHTML]=\"empty.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"empty\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose [compose]=\"empty\"></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"empty\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span [ngClass]=\"empty.class\" [style.cssText]=\"empty.style\">{{\n empty.value\n }}</span>\n }\n }\n }\n }\n </div>\n }\n }\n\n <ng-template\n #rowLayoutNode\n let-node\n let-slot=\"slot\"\n let-imageAlt=\"imageAlt\"\n >\n @if (\n simpleRichContentNodes(node, { slot: slot, imageAlt: imageAlt });\n as richNodes\n ) {\n <praxis-rich-content [nodes]=\"richNodes\"></praxis-rich-content>\n } @else {\n @switch (node?.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"node?.value\"\n [ngClass]=\"node?.class\"\n [color]=\"isThemeColor(node?.color) ? node?.color : undefined\"\n [style.cssText]=\"\n (node?.style || '' ? node.style + ';' : '') +\n iconStyle(node?.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <span\n class=\"inline-image\"\n [ngClass]=\"node?.class\"\n [style.cssText]=\"node?.style\"\n >\n <img\n [src]=\"node?.value\"\n [alt]=\"node?.imageAlt || imageAlt || ''\"\n />\n @if (node?.badge?.value) {\n <mat-chip\n class=\"inline-badge\"\n [color]=\"\n isThemeColor(node?.badge?.color)\n ? node?.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (node?.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(node?.badge?.color, node?.badge?.variant)\n \"\n >\n {{ node?.badge?.value }}\n </mat-chip>\n }\n </span>\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"isThemeColor(node?.color) ? node?.color : undefined\"\n [ngClass]=\"[\n node?.class || '',\n (node?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : '',\n ]\"\n [style.cssText]=\"\n chipStyle(node?.color, node?.variant) +\n (node?.style ? ';' + node.style : '')\n \"\n >\n {{ node?.value }}\n </mat-chip>\n }\n @case (\"rating\") {\n <span [ngClass]=\"node?.class\" [style.cssText]=\"node?.style\">\n @for (_ of ratingRange(node); track $index; let idx = $index) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, node?.value)\"\n [color]=\"ratingThemeColor(node)\"\n [style.cssText]=\"ratingIconStyle(node)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"node?.class\"\n [style.cssText]=\"node?.style\"\n [innerHTML]=\"node?.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"node\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose [compose]=\"node\"></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"node\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span [ngClass]=\"node?.class\" [style.cssText]=\"node?.style\">\n @if (\n slot === \"meta\" && config.templating?.metaPrefixIcon;\n as mpi\n ) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n {{ node?.value }}\n </span>\n }\n }\n }\n </ng-template>\n\n <ng-template #featureContent let-item let-feature=\"feature\">\n @if (featureTemplateNode(item, feature); as featureNode) {\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: featureNode,\n slot: 'feature',\n imageAlt: feature?.imageAlt || '',\n }\n \"\n ></ng-container>\n } @else if (featureRichContentNodes(item, feature); as featureNodes) {\n <praxis-rich-content [nodes]=\"featureNodes\"></praxis-rich-content>\n } @else {\n @if (feature.icon && featuresMode() !== \"labels-only\") {\n <mat-icon [praxisIcon]=\"feature.icon\"></mat-icon>\n }\n @if (featuresMode() !== \"icons-only\") {\n <span [ngClass]=\"feature.class\" [style.cssText]=\"feature.style\">\n @for (line of featureLabelLines(item, feature.expr); track $index) {\n <span class=\"feature-line\">{{ line }}</span>\n }\n </span>\n }\n }\n @if (\n featureProgressPercent(item, feature.expr, feature.class);\n as progress\n ) {\n <span class=\"feature-progress\" aria-hidden=\"true\">\n <span\n class=\"feature-progress__value\"\n [style.width.%]=\"progress\"\n ></span>\n </span>\n }\n </ng-template>\n\n <ng-template\n #listRowLayout\n let-item\n let-index=\"index\"\n let-sectionKey=\"sectionKey\"\n let-clickable=\"clickable\"\n >\n <div\n class=\"list-item-content\"\n [ngClass]=\"rowLayoutItemClass(item)\"\n [attr.style]=\"rowLayoutItemStyle(item)\"\n [attr.role]=\"clickable ? 'button' : null\"\n [attr.tabindex]=\"clickable ? '0' : null\"\n [attr.aria-label]=\"clickable ? itemAriaLabel(item) : null\"\n [attr.aria-expanded]=\"\n clickable && expansionOwnedByRow() ? isExpanded(item, index) : null\n \"\n [attr.aria-controls]=\"\n clickable && expansionOwnedByRow()\n ? expandRegionId(item, index)\n : null\n \"\n (click)=\"clickable ? onRowActivate(item, index, sectionKey) : null\"\n (keydown.enter)=\"\n clickable ? onRowActivate(item, index, sectionKey) : null\n \"\n (keydown.space)=\"\n onRowSpaceActivate($event, clickable, item, index, sectionKey)\n \"\n >\n @for (\n column of rowLayoutColumns();\n track rowLayoutTrackColumn($index, column)\n ) {\n <div\n [attr.data-row-slot]=\"column.slot\"\n [ngClass]=\"rowLayoutColumnClass(column)\"\n [attr.style]=\"rowLayoutColumnStyle(column)\"\n >\n @if (column.slot === \"leading\") {\n @if (rowLayoutSlot(item, column.slot); as lead) {\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: lead,\n slot: column.slot,\n imageAlt: rowLayoutImageAlt(column.slot),\n }\n \"\n ></ng-container>\n } @else {\n <span class=\"leading-placeholder\"></span>\n }\n } @else if (column.slot === \"trailing\") {\n @if (rowLayoutSlot(item, column.slot); as trailingNode) {\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: trailingNode,\n slot: column.slot,\n imageAlt: rowLayoutImageAlt(column.slot),\n }\n \"\n ></ng-container>\n }\n @if (showExpandIcon(\"trailing\")) {\n <button\n type=\"button\"\n [praxisIconButton]=\"\n isExpanded(item, index) ? 'expand_less' : 'expand_more'\n \"\n size=\"compact\"\n class=\"expand-toggle\"\n [attr.aria-label]=\"expandToggleAriaLabel(item, index)\"\n [attr.aria-expanded]=\"\n expansionOwnedByIcon() ? isExpanded(item, index) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByIcon() ? expandRegionId(item, index) : null\n \"\n (click)=\"onExpandToggle($event, item, index)\"\n ></button>\n }\n @if ((visibleActions(item, \"trailing\") || []).length) {\n <div\n class=\"list-item-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(item, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, item, index)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"onActionClick($event, action.id, item, index)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'stroked')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'raised')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant || action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'flat')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n } @else if (column.slot === \"actions\") {\n @if ((visibleActions(item, \"actions\") || []).length) {\n <div\n class=\"list-item-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(item, \"actions\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, item, index)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"onActionClick($event, action.id, item, index)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'stroked')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'raised')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant || action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'flat')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n } @else if (column.slot === \"expand\") {\n @if (showExpandIcon(\"expand\")) {\n <button\n type=\"button\"\n [praxisIconButton]=\"\n isExpanded(item, index) ? 'expand_less' : 'expand_more'\n \"\n size=\"compact\"\n class=\"expand-toggle\"\n [attr.aria-label]=\"expandToggleAriaLabel(item, index)\"\n [attr.aria-expanded]=\"\n expansionOwnedByIcon() ? isExpanded(item, index) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByIcon() ? expandRegionId(item, index) : null\n \"\n (click)=\"onExpandToggle($event, item, index)\"\n ></button>\n }\n } @else if (rowLayoutSlot(item, column.slot); as slotNode) {\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: slotNode,\n slot: column.slot,\n imageAlt: rowLayoutImageAlt(column.slot),\n }\n \"\n ></ng-container>\n }\n </div>\n }\n </div>\n </ng-template>\n\n <!-- List variant -->\n @if (isListVariant()) {\n <!-- Selection list -->\n @if (isSelectionEnabled()) {\n <mat-selection-list\n [multiple]=\"config.selection?.mode === 'multiple'\"\n [formControl]=\"boundControl\"\n (selectionChange)=\"onSelectionChange($event)\"\n >\n @for (\n section of sections$ | async;\n track trackBySection($index, section)\n ) {\n @if (section.key) {\n <div class=\"section-header mat-subheader\">\n @if (sectionHeaderTemplate(section.key); as sh) {\n @if (\n simpleRichContentNodes(sh, {\n imageAlt:\n config.templating?.sectionHeader?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (sh.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"sh.value\"\n [ngClass]=\"sh.class\"\n [color]=\"\n isThemeColor(sh.color) ? sh.color : undefined\n \"\n [style.cssText]=\"\n (sh.style ? sh.style + ';' : '') +\n iconStyle(sh.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <span\n class=\"inline-image\"\n [ngClass]=\"sh.class\"\n [style.cssText]=\"sh.style\"\n >\n <img\n [src]=\"sh.value\"\n [alt]=\"\n config.templating?.sectionHeader?.imageAlt || ''\n \"\n />\n @if (sh.badge?.value) {\n <mat-chip\n class=\"inline-badge\"\n [color]=\"\n isThemeColor(sh.badge?.color)\n ? sh.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (sh.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(sh.badge?.color, sh.badge?.variant)\n \"\n >{{ sh.badge?.value }}</mat-chip\n >\n }\n </span>\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(sh.color) ? sh.color : undefined\n \"\n [ngClass]=\"[\n sh.class || '',\n (sh.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : '',\n ]\"\n [style.cssText]=\"\n chipStyle(sh.color, sh.variant) +\n (sh.style ? ';' + sh.style : '')\n \"\n >{{ sh.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n <span [ngClass]=\"sh.class\" [style.cssText]=\"sh.style\">\n @for (\n _ of ratingRange(sh);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, sh.value)\"\n [color]=\"ratingThemeColor(sh)\"\n [style.cssText]=\"ratingIconStyle(sh)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"sh.class\"\n [style.cssText]=\"sh.style\"\n [innerHTML]=\"sh.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"sh\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"sh\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"sh\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span [ngClass]=\"sh.class\" [style.cssText]=\"sh.style\">{{\n sh.value\n }}</span>\n }\n }\n }\n }\n </div>\n }\n @for (\n item of section.items;\n track trackByItem($index, item);\n let i = $index\n ) {\n <mat-list-option\n [value]=\"item\"\n [attr.aria-label]=\"itemAriaLabel(item)\"\n >\n @if (hasRowLayoutGrid()) {\n <ng-container\n *ngTemplateOutlet=\"\n listRowLayout;\n context: {\n $implicit: item,\n index: i,\n sectionKey: section.key || undefined,\n clickable: false,\n }\n \"\n ></ng-container>\n } @else {\n <div\n class=\"list-item-content\"\n [ngClass]=\"itemRuleClass(item)\"\n [attr.style]=\"itemRuleStyle(item)\"\n >\n <div class=\"list-item-leading\">\n @if (leading(item); as lead) {\n @if (\n simpleRichContentNodes(lead, {\n imageAlt:\n config.templating?.leading?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"lead.value\"\n [ngClass]=\"lead.class\"\n [color]=\"\n isThemeColor(lead.color)\n ? lead.color\n : undefined\n \"\n [style.cssText]=\"\n (lead.style ? lead.style + ';' : '') +\n iconStyle(lead.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >\n <img\n [src]=\"lead.value\"\n [alt]=\"\n config.templating?.leading?.imageAlt || ''\n \"\n />\n @if (lead.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(lead.badge?.color)\n ? lead.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (lead.badge?.variant || 'filled') ===\n 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n lead.badge?.color,\n lead.badge?.variant\n )\n \"\n >{{ lead.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"text\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(lead.color)\n ? lead.color\n : undefined\n \"\n [ngClass]=\"\n (lead.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(lead.color, lead.variant)\n \"\n >{{ lead.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >\n @for (\n _ of ratingRange(lead);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, lead.value)\"\n [color]=\"ratingThemeColor(lead)\"\n [style.cssText]=\"ratingIconStyle(lead)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n [innerHTML]=\"lead.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"lead\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"lead\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"lead\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n }\n }\n } @else {\n <span class=\"leading-placeholder\"></span>\n }\n </div>\n <div class=\"list-item-text\">\n <div\n class=\"primary\"\n [ngClass]=\"primary(item)?.class\"\n [style.cssText]=\"primary(item)?.style\"\n >\n @if (\n simpleRichContentNodes(primary(item));\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (primary(item)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"primary(item)\"\n ></praxis-list-metric>\n } @else if (primary(item)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"primary(item)\"\n ></praxis-list-compose>\n } @else if (primary(item)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"primary(item)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ primary(item)?.value }}\n }\n </div>\n @if (layoutLines > 1) {\n <div\n class=\"secondary\"\n [ngClass]=\"secondary(item)?.class\"\n [style.cssText]=\"secondary(item)?.style\"\n >\n @if (\n simpleRichContentNodes(secondary(item));\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (secondary(item)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"secondary(item)\"\n ></praxis-list-metric>\n } @else if (secondary(item)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"secondary(item)\"\n ></praxis-list-compose>\n } @else if (secondary(item)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"secondary(item)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ secondary(item)?.value }}\n }\n </div>\n }\n @if (meta(item); as m) {\n @if (\n (config.templating?.metaPlacement === \"line\" &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")) ||\n (layoutLines > 2 &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\"))\n ) {\n <div\n class=\"tertiary\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (config.templating?.metaPrefixIcon; as mpi) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n {{ m.value }}\n </div>\n }\n }\n @if (featuresVisible()) {\n <div class=\"features\">\n @for (\n f of config.templating?.features || [];\n track $index;\n let fi = $index\n ) {\n <span\n class=\"feature\"\n [ngClass]=\"\n featureSemanticClass(item, f.expr, f.class)\n \"\n >\n <ng-container\n *ngTemplateOutlet=\"\n featureContent;\n context: { $implicit: item, feature: f }\n \"\n ></ng-container>\n </span>\n }\n </div>\n }\n </div>\n <div class=\"list-item-trailing\">\n @if (meta(item); as m) {\n @if (\n !(\n (config.templating?.metaPlacement === \"line\" ||\n layoutLines > 2) &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")\n )\n ) {\n <div\n class=\"meta\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (\n simpleRichContentNodes(m, { slot: \"meta\" });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(m.color)\n ? m.color\n : undefined\n \"\n [ngClass]=\"\n (m.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(m.color, m.variant)\n \"\n >{{ m.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(m);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, m.value)\"\n [color]=\"ratingThemeColor(m)\"\n [style.cssText]=\"ratingIconStyle(m)\"\n ></mat-icon>\n }\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"m.value\"\n [color]=\"\n isThemeColor(m.color)\n ? m.color\n : undefined\n \"\n [style.cssText]=\"iconStyle(m.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img\n [src]=\"m.value\"\n [alt]=\"m.imageAlt || ''\"\n /></span>\n }\n @case (\"html\") {\n <span [innerHTML]=\"m.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"m\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"m\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"m\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>\n @if (\n config.templating?.metaPrefixIcon;\n as mpi2\n ) {\n <mat-icon [praxisIcon]=\"mpi2\"></mat-icon>\n }\n {{ m.value }}</span\n >\n }\n }\n }\n </div>\n }\n }\n @if (trailing(item); as tr) {\n <div\n class=\"trailing\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n @if (\n simpleRichContentNodes(tr, {\n imageAlt:\n config.templating?.trailing?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (tr.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr.color)\n ? tr.color\n : undefined\n \"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(tr.color, tr.variant)\n \"\n >{{ tr.value }}</mat-chip\n >\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"\n isThemeColor(tr.color)\n ? tr.color\n : undefined\n \"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n <img\n [src]=\"tr.value\"\n [alt]=\"\n config.templating?.trailing?.imageAlt ||\n ''\n \"\n />\n @if (tr.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(tr.badge?.color)\n ? tr.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (tr.badge?.variant || 'filled') ===\n 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n tr.badge?.color,\n tr.badge?.variant\n )\n \"\n >{{ tr.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(tr);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, tr.value)\"\n [color]=\"ratingThemeColor(tr)\"\n [style.cssText]=\"ratingIconStyle(tr)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n @if (\n item.alertsData !== undefined &&\n item.ownerName\n ) {\n <!-- Component-based rendering for executive trailing -->\n <span class=\"exec-trailing-shell\">\n <praxis-executive-alerts\n [alerts]=\"item.alertsData\"\n ></praxis-executive-alerts>\n <praxis-executive-owner\n [name]=\"item.ownerName\"\n ></praxis-executive-owner>\n </span>\n } @else {\n <span [innerHTML]=\"tr.value\"></span>\n }\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"tr\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"tr\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"tr\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>{{ tr.value }}</span>\n }\n }\n }\n </div>\n }\n @if (showExpandIcon(\"trailing\")) {\n <button\n type=\"button\"\n [praxisIconButton]=\"\n isExpanded(item, i) ? 'expand_less' : 'expand_more'\n \"\n size=\"compact\"\n class=\"expand-toggle\"\n [attr.aria-label]=\"expandToggleAriaLabel(item, i)\"\n [attr.aria-expanded]=\"\n expansionOwnedByIcon() ? isExpanded(item, i) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByIcon()\n ? expandRegionId(item, i)\n : null\n \"\n (click)=\"onExpandToggle($event, item, i)\"\n ></button>\n }\n @if ((visibleActions(item, \"trailing\") || []).length) {\n <div\n class=\"list-item-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(item, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, item, i)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'stroked')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'raised')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant ||\n action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'flat')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n </div>\n </div>\n }\n @if (isExpandable() && isExpanded(item, i)) {\n <div\n class=\"item-expansion\"\n [ngClass]=\"expansionRenderingClasses()\"\n [style]=\"expansionRenderingStyle()\"\n [id]=\"expandRegionId(item, i)\"\n role=\"region\"\n [attr.aria-label]=\"expansionRegionAriaLabel(item)\"\n >\n <div class=\"item-expansion-grid\">\n @for (\n expansionSection of expansionSections(item);\n track expansionSection.id\n ) {\n <section\n class=\"expansion-section\"\n [attr.data-section-type]=\"expansionSection.type\"\n >\n @if (expansionSection.title) {\n <div class=\"expansion-section-title\">\n {{ expansionSection.title }}\n </div>\n }\n @if (\n expansionSectionHasContent(\n item,\n expansionSection,\n i\n )\n ) {\n @switch (expansionSection.type) {\n @case (\"chip-list\") {\n <div class=\"expansion-chip-list\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <mat-chip class=\"expansion-chip\">{{\n expansionItemLabel(expansionEntry)\n }}</mat-chip>\n }\n </div>\n }\n @case (\"timeline\") {\n <div class=\"expansion-timeline\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-timeline-item\">\n <div class=\"expansion-timeline-title\">\n {{\n expansionTimelineTitle(expansionEntry)\n }}\n </div>\n @if (\n expansionTimelineMeta(expansionEntry)\n ) {\n <div class=\"expansion-timeline-meta\">\n {{\n expansionTimelineMeta(\n expansionEntry\n )\n }}\n </div>\n }\n @if (\n expansionTimelineDescription(\n expansionEntry\n )\n ) {\n <div\n class=\"expansion-timeline-description\"\n >\n {{\n expansionTimelineDescription(\n expansionEntry\n )\n }}\n </div>\n }\n </div>\n }\n </div>\n }\n @case (\"key-value\") {\n <dl class=\"expansion-key-value\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-key-value-row\">\n @if (\n expansionKeyValueKey(expansionEntry)\n ) {\n <dt>\n {{\n expansionKeyValueKey(expansionEntry)\n }}\n </dt>\n }\n <dd>\n {{\n expansionKeyValueValue(expansionEntry)\n }}\n </dd>\n </div>\n }\n </dl>\n }\n @case (\"rich-content\") {\n @if (\n expansionRichContentDocument(\n item,\n expansionSection,\n i\n );\n as richExpansionDocument\n ) {\n <praxis-rich-content\n [document]=\"richExpansionDocument\"\n [context]=\"\n expansionRichContentContext(\n item,\n expansionSection,\n i\n )\n \"\n [hostCapabilities]=\"\n expansionRichContentHostCapabilities(\n item,\n expansionSection,\n i\n )\n \"\n rootClassName=\"praxis-list-expansion-rich-content\"\n ></praxis-rich-content>\n }\n }\n @default {\n <div class=\"expansion-info-list\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-info-item\">\n <div class=\"expansion-info-title\">\n {{ expansionInfoTitle(expansionEntry) }}\n </div>\n @if (expansionInfoValue(expansionEntry)) {\n <div class=\"expansion-info-value\">\n {{\n expansionInfoValue(expansionEntry)\n }}\n </div>\n }\n </div>\n }\n </div>\n }\n }\n } @else {\n <div class=\"expansion-empty\">\n {{\n expansionSection.emptyLabel ||\n \"Sem dados dispon\u00EDveis\"\n }}\n </div>\n }\n </section>\n }\n </div>\n </div>\n }\n </mat-list-option>\n @if (\n (config.layout?.dividers === \"all\" ||\n config.layout?.dividers === \"between\") &&\n i < section.items.length - 1\n ) {\n <mat-divider></mat-divider>\n }\n }\n }\n </mat-selection-list>\n } @else {\n <ng-container *ngTemplateOutlet=\"readList\"></ng-container>\n }\n\n <!-- Read-only list -->\n <ng-template #readList>\n <mat-list>\n @for (\n section of sections$ | async;\n track trackBySection($index, section);\n let sidx = $index\n ) {\n @if (section.key) {\n <div class=\"section-header mat-subheader\">\n @if (sectionHeaderTemplate(section.key); as sh) {\n @if (\n simpleRichContentNodes(sh, {\n imageAlt:\n config.templating?.sectionHeader?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (sh.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"sh.value\"\n [ngClass]=\"sh.class\"\n [color]=\"\n isThemeColor(sh.color) ? sh.color : undefined\n \"\n [style.cssText]=\"\n (sh.style ? sh.style + ';' : '') +\n iconStyle(sh.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <span\n class=\"inline-image\"\n [ngClass]=\"sh.class\"\n [style.cssText]=\"sh.style\"\n >\n <img\n [src]=\"sh.value\"\n [alt]=\"\n config.templating?.sectionHeader?.imageAlt || ''\n \"\n />\n @if (sh.badge?.value) {\n <mat-chip\n class=\"inline-badge\"\n [color]=\"\n isThemeColor(sh.badge?.color)\n ? sh.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (sh.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(sh.badge?.color, sh.badge?.variant)\n \"\n >{{ sh.badge?.value }}</mat-chip\n >\n }\n </span>\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(sh.color) ? sh.color : undefined\n \"\n [ngClass]=\"[\n sh.class || '',\n (sh.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : '',\n ]\"\n [style.cssText]=\"\n chipStyle(sh.color, sh.variant) +\n (sh.style ? ';' + sh.style : '')\n \"\n >{{ sh.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n <span [ngClass]=\"sh.class\" [style.cssText]=\"sh.style\">\n @for (\n _ of ratingRange(sh);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, sh.value)\"\n [color]=\"ratingThemeColor(sh)\"\n [style.cssText]=\"ratingIconStyle(sh)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"sh.class\"\n [style.cssText]=\"sh.style\"\n [innerHTML]=\"sh.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"sh\"></praxis-list-metric>\n }\n @default {\n <span [ngClass]=\"sh.class\" [style.cssText]=\"sh.style\">{{\n sh.value\n }}</span>\n }\n }\n }\n }\n </div>\n }\n @for (\n item of section.items;\n track trackByItem($index, item);\n let i = $index\n ) {\n <mat-list-item [attr.data-item-status]=\"item?.status || null\">\n @if (hasRowLayoutGrid()) {\n <ng-container\n *ngTemplateOutlet=\"\n listRowLayout;\n context: {\n $implicit: item,\n index: i,\n sectionKey: section.key || undefined,\n clickable: true,\n }\n \"\n ></ng-container>\n } @else {\n <div\n class=\"list-item-content\"\n [ngClass]=\"itemRuleClass(item)\"\n [attr.style]=\"itemRuleStyle(item)\"\n >\n <button\n type=\"button\"\n class=\"list-item-main-action\"\n [attr.aria-label]=\"itemAriaLabel(item)\"\n [attr.aria-expanded]=\"\n expansionOwnedByRow() ? isExpanded(item, i) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByRow() ? expandRegionId(item, i) : null\n \"\n (click)=\"onRowActivate(item, i, section.key || undefined)\"\n >\n <div class=\"list-item-leading\">\n @if (leading(item); as lead) {\n @if (\n simpleRichContentNodes(lead, {\n imageAlt:\n config.templating?.leading?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"lead.value\"\n [ngClass]=\"lead.class\"\n [color]=\"\n isThemeColor(lead.color)\n ? lead.color\n : undefined\n \"\n [style.cssText]=\"\n (lead.style ? lead.style + ';' : '') +\n iconStyle(lead.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >\n <img\n [src]=\"lead.value\"\n [alt]=\"\n config.templating?.leading?.imageAlt || ''\n \"\n />\n @if (lead.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(lead.badge?.color)\n ? lead.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (lead.badge?.variant || 'filled') ===\n 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n lead.badge?.color,\n lead.badge?.variant\n )\n \"\n >{{ lead.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"text\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(lead.color)\n ? lead.color\n : undefined\n \"\n [ngClass]=\"\n (lead.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(lead.color, lead.variant)\n \"\n >{{ lead.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(lead);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, lead.value)\"\n [color]=\"ratingThemeColor(lead)\"\n [style.cssText]=\"ratingIconStyle(lead)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n [innerHTML]=\"lead.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"lead\"\n ></praxis-list-metric>\n }\n @default {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n }\n }\n } @else {\n <span class=\"leading-placeholder\"></span>\n }\n </div>\n <div class=\"list-item-text\">\n <div\n class=\"primary\"\n [ngClass]=\"primary(item)?.class\"\n [style.cssText]=\"primary(item)?.style\"\n >\n @if (\n simpleRichContentNodes(primary(item));\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (primary(item)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"primary(item)\"\n ></praxis-list-metric>\n } @else if (primary(item)?.type === \"html\") {\n <span [innerHTML]=\"primary(item)?.value\"></span>\n } @else {\n {{ primary(item)?.value }}\n }\n </div>\n @if (layoutLines > 1) {\n <div\n class=\"secondary\"\n [ngClass]=\"secondary(item)?.class\"\n [style.cssText]=\"secondary(item)?.style\"\n >\n @if (\n simpleRichContentNodes(secondary(item));\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (\n item.segmentVariant &&\n item.segmentLabel &&\n item.accountType &&\n item.since\n ) {\n <!-- Component-based rendering for executive items -->\n <span class=\"exec-subline\">\n <praxis-executive-badge\n [variant]=\"item.segmentVariant\"\n [label]=\"item.segmentLabel\"\n ></praxis-executive-badge>\n <span class=\"exec-subcopy\">{{\n item.accountType\n }}</span>\n <span class=\"exec-subseparator\">\u00B7</span>\n <span class=\"exec-subcopy\"\n >Desde {{ item.since }}</span\n >\n </span>\n } @else if (secondary(item)?.type === \"html\") {\n <span [innerHTML]=\"secondary(item)?.value\"></span>\n } @else if (secondary(item)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"secondary(item)\"\n ></praxis-list-metric>\n } @else {\n {{ secondary(item)?.value }}\n }\n </div>\n }\n @if (meta(item); as m) {\n @if (\n (config.templating?.metaPlacement === \"line\" &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")) ||\n (layoutLines > 2 &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\"))\n ) {\n <div\n class=\"tertiary\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (config.templating?.metaPrefixIcon; as mpi) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n {{ m.value }}\n </div>\n }\n }\n @if (featuresVisible()) {\n <div class=\"features\">\n @for (\n f of config.templating?.features || [];\n track $index;\n let fi = $index\n ) {\n <span\n class=\"feature\"\n [ngClass]=\"\n featureSemanticClass(item, f.expr, f.class)\n \"\n >\n <ng-container\n *ngTemplateOutlet=\"\n featureContent;\n context: { $implicit: item, feature: f }\n \"\n ></ng-container>\n </span>\n }\n </div>\n }\n </div>\n </button>\n <div class=\"list-item-trailing\">\n @if (meta(item); as m) {\n @if (\n !(\n (config.templating?.metaPlacement === \"line\" ||\n layoutLines > 2) &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")\n )\n ) {\n <div\n class=\"meta\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (\n simpleRichContentNodes(m, { slot: \"meta\" });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(m.color)\n ? m.color\n : undefined\n \"\n [ngClass]=\"\n (m.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(m.color, m.variant)\n \"\n >{{ m.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(m);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, m.value)\"\n [color]=\"ratingThemeColor(m)\"\n [style.cssText]=\"ratingIconStyle(m)\"\n ></mat-icon>\n }\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"m.value\"\n [color]=\"\n isThemeColor(m.color)\n ? m.color\n : undefined\n \"\n [style.cssText]=\"iconStyle(m.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img\n [src]=\"m.value\"\n [alt]=\"m.imageAlt || ''\"\n /></span>\n }\n @case (\"html\") {\n <span [innerHTML]=\"m.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"m\"\n ></praxis-list-metric>\n }\n @default {\n <span>\n @if (\n config.templating?.metaPrefixIcon;\n as mpi2\n ) {\n <mat-icon [praxisIcon]=\"mpi2\"></mat-icon>\n }\n {{ m.value }}</span\n >\n }\n }\n }\n </div>\n }\n }\n @if (trailing(item); as tr) {\n <div\n class=\"trailing\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n @if (\n simpleRichContentNodes(tr, {\n imageAlt:\n config.templating?.trailing?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (tr.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr.color)\n ? tr.color\n : undefined\n \"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(tr.color, tr.variant)\n \"\n >{{ tr.value }}</mat-chip\n >\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"\n isThemeColor(tr.color)\n ? tr.color\n : undefined\n \"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n <img\n [src]=\"tr.value\"\n [alt]=\"\n config.templating?.trailing?.imageAlt ||\n ''\n \"\n />\n @if (tr.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(tr.badge?.color)\n ? tr.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (tr.badge?.variant || 'filled') ===\n 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n tr.badge?.color,\n tr.badge?.variant\n )\n \"\n >{{ tr.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(tr);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, tr.value)\"\n [color]=\"ratingThemeColor(tr)\"\n [style.cssText]=\"ratingIconStyle(tr)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n @if (\n item.alertsData !== undefined &&\n item.ownerName\n ) {\n <!-- Component-based rendering for executive trailing -->\n <span class=\"exec-trailing-shell\">\n <praxis-executive-alerts\n [alerts]=\"item.alertsData\"\n ></praxis-executive-alerts>\n <praxis-executive-owner\n [name]=\"item.ownerName\"\n ></praxis-executive-owner>\n </span>\n } @else {\n <span [innerHTML]=\"tr.value\"></span>\n }\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"tr\"\n ></praxis-list-metric>\n }\n @default {\n <span>{{ tr.value }}</span>\n }\n }\n }\n </div>\n }\n @if (showExpandIcon(\"trailing\")) {\n <button\n type=\"button\"\n [praxisIconButton]=\"\n isExpanded(item, i) ? 'expand_less' : 'expand_more'\n \"\n size=\"compact\"\n class=\"expand-toggle\"\n [attr.aria-label]=\"expandToggleAriaLabel(item, i)\"\n [attr.aria-expanded]=\"\n expansionOwnedByIcon() ? isExpanded(item, i) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByIcon()\n ? expandRegionId(item, i)\n : null\n \"\n (click)=\"onExpandToggle($event, item, i)\"\n ></button>\n }\n @if ((visibleActions(item, \"trailing\") || []).length) {\n <div\n class=\"list-item-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(item, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, item, i)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'stroked')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'raised')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant ||\n action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'flat')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n </div>\n </div>\n }\n @if (isExpandable() && isExpanded(item, i)) {\n <div\n class=\"item-expansion\"\n [ngClass]=\"expansionRenderingClasses()\"\n [style]=\"expansionRenderingStyle()\"\n [id]=\"expandRegionId(item, i)\"\n role=\"region\"\n [attr.aria-label]=\"expansionRegionAriaLabel(item)\"\n >\n <div class=\"item-expansion-grid\">\n @for (\n expansionSection of expansionSections(item);\n track expansionSection.id\n ) {\n <section\n class=\"expansion-section\"\n [attr.data-section-type]=\"expansionSection.type\"\n >\n @if (expansionSection.title) {\n <div class=\"expansion-section-title\">\n {{ expansionSection.title }}\n </div>\n }\n @if (\n expansionSectionHasContent(\n item,\n expansionSection,\n i\n )\n ) {\n @switch (expansionSection.type) {\n @case (\"chip-list\") {\n <div class=\"expansion-chip-list\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <mat-chip class=\"expansion-chip\">{{\n expansionItemLabel(expansionEntry)\n }}</mat-chip>\n }\n </div>\n }\n @case (\"timeline\") {\n <div class=\"expansion-timeline\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-timeline-item\">\n <div class=\"expansion-timeline-title\">\n {{\n expansionTimelineTitle(expansionEntry)\n }}\n </div>\n @if (\n expansionTimelineMeta(expansionEntry)\n ) {\n <div class=\"expansion-timeline-meta\">\n {{\n expansionTimelineMeta(\n expansionEntry\n )\n }}\n </div>\n }\n @if (\n expansionTimelineDescription(\n expansionEntry\n )\n ) {\n <div\n class=\"expansion-timeline-description\"\n >\n {{\n expansionTimelineDescription(\n expansionEntry\n )\n }}\n </div>\n }\n </div>\n }\n </div>\n }\n @case (\"key-value\") {\n <dl class=\"expansion-key-value\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-key-value-row\">\n @if (\n expansionKeyValueKey(expansionEntry)\n ) {\n <dt>\n {{\n expansionKeyValueKey(expansionEntry)\n }}\n </dt>\n }\n <dd>\n {{\n expansionKeyValueValue(expansionEntry)\n }}\n </dd>\n </div>\n }\n </dl>\n }\n @case (\"rich-content\") {\n @if (\n expansionRichContentDocument(\n item,\n expansionSection,\n i\n );\n as richExpansionDocument\n ) {\n <praxis-rich-content\n [document]=\"richExpansionDocument\"\n [context]=\"\n expansionRichContentContext(\n item,\n expansionSection,\n i\n )\n \"\n [hostCapabilities]=\"\n expansionRichContentHostCapabilities(\n item,\n expansionSection,\n i\n )\n \"\n rootClassName=\"praxis-list-expansion-rich-content\"\n ></praxis-rich-content>\n }\n }\n @default {\n <div class=\"expansion-info-list\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-info-item\">\n <div class=\"expansion-info-title\">\n {{ expansionInfoTitle(expansionEntry) }}\n </div>\n @if (expansionInfoValue(expansionEntry)) {\n <div class=\"expansion-info-value\">\n {{\n expansionInfoValue(expansionEntry)\n }}\n </div>\n }\n </div>\n }\n </div>\n }\n }\n } @else {\n <div class=\"expansion-empty\">\n {{\n expansionSection.emptyLabel ||\n \"Sem dados dispon\u00EDveis\"\n }}\n </div>\n }\n </section>\n }\n </div>\n </div>\n }\n </mat-list-item>\n @if (\n (config.layout?.dividers === \"all\" ||\n config.layout?.dividers === \"between\") &&\n i < section.items.length - 1\n ) {\n <mat-divider></mat-divider>\n }\n }\n }\n </mat-list>\n </ng-template>\n } @else if (isTilesVariant()) {\n <ng-container *ngTemplateOutlet=\"tilesVariant\"></ng-container>\n } @else {\n <ng-container *ngTemplateOutlet=\"cardsVariant\"></ng-container>\n }\n\n <!-- Cards variant -->\n <ng-template #cardsVariant>\n <div class=\"cards-grid\">\n @for (it of (items$ | async) ?? []; track $index; let i = $index) {\n <div\n class=\"item-card\"\n [ngClass]=\"itemRuleClass(it)\"\n [attr.style]=\"itemRuleStyle(it)\"\n >\n <div\n class=\"list-item-content card-main-action\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"itemAriaLabel(it)\"\n (click)=\"onItemClick(it, i)\"\n (keydown.enter)=\"onItemClick(it, i)\"\n (keydown.space)=\"$event.preventDefault(); onItemClick(it, i)\"\n >\n <div class=\"list-item-leading\">\n @if (leading(it); as lead) {\n @if (\n simpleRichContentNodes(lead, {\n imageAlt: config.templating?.leading?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"lead.value\"\n [ngClass]=\"lead.class\"\n [color]=\"\n isThemeColor(lead.color) ? lead.color : undefined\n \"\n [style.cssText]=\"\n (lead.style ? lead.style + ';' : '') +\n iconStyle(lead.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >\n <img\n [src]=\"lead.value\"\n [alt]=\"config.templating?.leading?.imageAlt || ''\"\n />\n @if (lead.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(lead.badge?.color)\n ? lead.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (lead.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n lead.badge?.color,\n lead.badge?.variant\n )\n \"\n >{{ lead.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"text\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(lead.color) ? lead.color : undefined\n \"\n [ngClass]=\"\n (lead.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(lead.color, lead.variant)\"\n >{{ lead.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(lead);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, lead.value)\"\n [color]=\"ratingThemeColor(lead)\"\n [style.cssText]=\"ratingIconStyle(lead)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n [innerHTML]=\"lead.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"lead\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"lead\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"lead\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n }\n }\n } @else {\n <span class=\"leading-placeholder\"></span>\n }\n </div>\n <div class=\"list-item-text\">\n <div\n class=\"primary\"\n [ngClass]=\"primary(it)?.class\"\n [style.cssText]=\"primary(it)?.style\"\n >\n @if (simpleRichContentNodes(primary(it)); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (primary(it)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"primary(it)\"\n ></praxis-list-metric>\n } @else if (primary(it)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"primary(it)\"\n ></praxis-list-compose>\n } @else if (primary(it)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"primary(it)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ primary(it)?.value }}\n }\n </div>\n @if (layoutLines > 1) {\n <div\n class=\"secondary\"\n [ngClass]=\"secondary(it)?.class\"\n [style.cssText]=\"secondary(it)?.style\"\n >\n @if (simpleRichContentNodes(secondary(it)); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (secondary(it)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"secondary(it)\"\n ></praxis-list-metric>\n } @else if (secondary(it)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"secondary(it)\"\n ></praxis-list-compose>\n } @else if (secondary(it)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"secondary(it)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ secondary(it)?.value }}\n }\n </div>\n }\n @if (meta(it); as m) {\n @if (\n (config.templating?.metaPlacement === \"line\" &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")) ||\n (layoutLines > 2 &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\"))\n ) {\n <div\n class=\"tertiary\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (config.templating?.metaPrefixIcon; as mpi) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n {{ m.value }}\n </div>\n }\n }\n @if (featuresVisible()) {\n <div class=\"features\">\n @for (\n f of config.templating?.features || [];\n track $index;\n let fi = $index\n ) {\n <span\n class=\"feature\"\n [ngClass]=\"featureSemanticClass(it, f.expr, f.class)\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n featureContent;\n context: { $implicit: it, feature: f }\n \"\n ></ng-container>\n </span>\n }\n </div>\n }\n @if (cardDetailSlots(it); as detailSlots) {\n @if (detailSlots.length) {\n <div class=\"card-detail-strip\">\n @for (detail of detailSlots; track detail.slot) {\n <div\n class=\"card-detail\"\n [ngClass]=\"'card-detail--' + detail.slot\"\n >\n <span class=\"card-detail__label\">{{\n detail.label\n }}</span>\n <span class=\"card-detail__value\">\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: detail.node,\n slot: detail.slot,\n imageAlt: rowLayoutImageAlt(detail.slot),\n }\n \"\n ></ng-container>\n </span>\n </div>\n }\n </div>\n }\n }\n </div>\n <div class=\"list-item-trailing\">\n @if (meta(it); as m) {\n @if (\n !(\n (config.templating?.metaPlacement === \"line\" ||\n layoutLines > 2) &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")\n )\n ) {\n <div\n class=\"meta\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (\n simpleRichContentNodes(m, {\n slot: \"meta\",\n imageAlt: m.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(m.color) ? m.color : undefined\n \"\n [ngClass]=\"\n (m.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(m.color, m.variant)\"\n >{{ m.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(m);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, m.value)\"\n [color]=\"ratingThemeColor(m)\"\n [style.cssText]=\"ratingIconStyle(m)\"\n ></mat-icon>\n }\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"m.value\"\n [color]=\"\n isThemeColor(m.color) ? m.color : undefined\n \"\n [style.cssText]=\"iconStyle(m.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img [src]=\"m.value\" [alt]=\"m.imageAlt || ''\"\n /></span>\n }\n @case (\"html\") {\n <span [innerHTML]=\"m.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"m\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"m\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"m\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>\n @if (config.templating?.metaPrefixIcon; as mpi2) {\n <mat-icon [praxisIcon]=\"mpi2\"></mat-icon>\n }\n {{ m.value }}</span\n >\n }\n }\n }\n </div>\n }\n }\n @if (trailing(it); as tr) {\n @if (\n (config.templating?.statusPosition || \"inline\") ===\n \"top-right\" &&\n (tr?.type === \"chip\" || tr?.type === \"icon\")\n ) {\n <div class=\"status-overlay\">\n @if (simpleRichContentNodes(tr); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @if (tr?.type === \"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(tr.color, tr.variant)\"\n >{{ tr.value }}</mat-chip\n >\n }\n @if (tr?.type === \"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n }\n </div>\n } @else {\n <div\n class=\"trailing\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n @if (\n simpleRichContentNodes(tr, {\n imageAlt: config.templating?.trailing?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (tr.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(tr.color, tr.variant)\"\n >{{ tr.value }}</mat-chip\n >\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n <img\n [src]=\"tr.value\"\n [alt]=\"\n config.templating?.trailing?.imageAlt || ''\n \"\n />\n @if (tr.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(tr.badge?.color)\n ? tr.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (tr.badge?.variant || 'filled') ===\n 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n tr.badge?.color,\n tr.badge?.variant\n )\n \"\n >{{ tr.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(tr);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, tr.value)\"\n [color]=\"ratingThemeColor(tr)\"\n [style.cssText]=\"ratingIconStyle(tr)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n <span [innerHTML]=\"tr.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"tr\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"tr\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"tr\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>{{ tr.value }}</span>\n }\n }\n }\n </div>\n }\n }\n </div>\n </div>\n <div\n class=\"card-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(it, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, it, i)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'stroked')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'raised')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant || action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'flat')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n </div>\n }\n </div>\n </ng-template>\n\n <!-- Tiles variant -->\n <ng-template #tilesVariant>\n <div class=\"tiles-grid\">\n @for (it of (items$ | async) ?? []; track $index; let i = $index) {\n <div\n class=\"item-tile\"\n [ngClass]=\"itemRuleClass(it)\"\n [attr.style]=\"itemRuleStyle(it)\"\n >\n <div\n class=\"tile-main-action\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"itemAriaLabel(it)\"\n (click)=\"onItemClick(it, i)\"\n (keydown.enter)=\"onItemClick(it, i)\"\n (keydown.space)=\"$event.preventDefault(); onItemClick(it, i)\"\n >\n <div class=\"tile-media\">\n @if (leading(it); as lead) {\n @if (\n simpleRichContentNodes(lead, {\n imageAlt: config.templating?.leading?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case (\"image\") {\n <img\n [src]=\"lead.value\"\n [alt]=\"config.templating?.leading?.imageAlt || ''\"\n />\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"lead.value\"\n [ngClass]=\"lead.class\"\n [color]=\"\n isThemeColor(lead.color) ? lead.color : undefined\n \"\n [style.cssText]=\"\n (lead.style ? lead.style + ';' : '') +\n iconStyle(lead.color)\n \"\n ></mat-icon>\n }\n @case (\"text\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(lead.color) ? lead.color : undefined\n \"\n [ngClass]=\"\n (lead.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(lead.color, lead.variant)\"\n >{{ lead.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n <span [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">\n @for (\n _ of ratingRange(lead);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, lead.value)\"\n [color]=\"ratingThemeColor(lead)\"\n [style.cssText]=\"ratingIconStyle(lead)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n [innerHTML]=\"lead.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"lead\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"lead\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"lead\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n }\n }\n }\n </div>\n\n @if (trailing(it); as tr) {\n @if (\n (config.templating?.statusPosition || \"inline\") ===\n \"top-right\" &&\n (tr?.type === \"chip\" || tr?.type === \"icon\")\n ) {\n <div class=\"tile-status\">\n @if (simpleRichContentNodes(tr); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @if (tr?.type === \"chip\") {\n <mat-chip\n [color]=\"isThemeColor(tr.color) ? tr.color : undefined\"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(tr.color, tr.variant)\"\n >{{ tr.value }}</mat-chip\n >\n }\n @if (tr?.type === \"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"isThemeColor(tr.color) ? tr.color : undefined\"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n }\n </div>\n }\n }\n\n <div class=\"tile-body\">\n <div\n class=\"primary\"\n [ngClass]=\"primary(it)?.class\"\n [style.cssText]=\"primary(it)?.style\"\n >\n @if (simpleRichContentNodes(primary(it)); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (primary(it)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"primary(it)\"\n ></praxis-list-metric>\n } @else if (primary(it)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"primary(it)\"\n ></praxis-list-compose>\n } @else if (primary(it)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"primary(it)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ primary(it)?.value }}\n }\n </div>\n @if (layoutLines > 1) {\n <div\n class=\"secondary\"\n [ngClass]=\"secondary(it)?.class\"\n [style.cssText]=\"secondary(it)?.style\"\n >\n @if (simpleRichContentNodes(secondary(it)); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (secondary(it)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"secondary(it)\"\n ></praxis-list-metric>\n } @else if (secondary(it)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"secondary(it)\"\n ></praxis-list-compose>\n } @else if (secondary(it)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"secondary(it)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ secondary(it)?.value }}\n }\n </div>\n }\n\n @if (meta(it); as m) {\n <div\n class=\"tile-meta\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (config.templating?.metaPrefixIcon; as mpi) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n @if (\n simpleRichContentNodes(m, {\n slot: \"meta\",\n imageAlt: m.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"isThemeColor(m.color) ? m.color : undefined\"\n [ngClass]=\"\n (m.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(m.color, m.variant)\"\n >{{ m.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(m);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, m.value)\"\n [color]=\"ratingThemeColor(m)\"\n [style.cssText]=\"ratingIconStyle(m)\"\n ></mat-icon>\n }\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"m.value\"\n [color]=\"isThemeColor(m.color) ? m.color : undefined\"\n [style.cssText]=\"iconStyle(m.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img [src]=\"m.value\" [alt]=\"m.imageAlt || ''\"\n /></span>\n }\n @case (\"html\") {\n <span [innerHTML]=\"m.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"m\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"m\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"m\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>{{ m.value }}</span>\n }\n }\n }\n </div>\n }\n\n @if (featuresVisible()) {\n <div class=\"tiles-features\">\n @for (\n f of config.templating?.features || [];\n track $index;\n let fi = $index\n ) {\n <span\n class=\"feature\"\n [ngClass]=\"featureSemanticClass(it, f.expr, f.class)\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n featureContent;\n context: { $implicit: it, feature: f }\n \"\n ></ng-container>\n </span>\n }\n </div>\n }\n\n @if (trailing(it); as tr2) {\n @if (\n !(\n (config.templating?.statusPosition || \"inline\") ===\n \"top-right\" &&\n (tr2?.type === \"chip\" || tr2?.type === \"icon\")\n )\n ) {\n <div\n class=\"tile-trailing\"\n [ngClass]=\"tr2.class\"\n [style.cssText]=\"tr2.style\"\n >\n @if (\n simpleRichContentNodes(tr2, {\n imageAlt: tr2.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (tr2.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr2.color) ? tr2.color : undefined\n \"\n [ngClass]=\"\n (tr2.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(tr2.color, tr2.variant)\"\n >{{ tr2.value }}</mat-chip\n >\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"tr2.value\"\n [color]=\"\n isThemeColor(tr2.color) ? tr2.color : undefined\n \"\n [style.cssText]=\"iconStyle(tr2.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img [src]=\"tr2.value\" [alt]=\"tr2.imageAlt || ''\"\n /></span>\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(tr2);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, tr2.value)\"\n [color]=\"ratingThemeColor(tr2)\"\n [style.cssText]=\"ratingIconStyle(tr2)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n <span [innerHTML]=\"tr2.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"tr2\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"tr2\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"tr2\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>{{ tr2.value }}</span>\n }\n }\n }\n </div>\n }\n }\n </div>\n </div>\n\n @if ((visibleActions(it, \"trailing\") || []).length) {\n <div\n class=\"tile-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(it, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, it, i)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'stroked')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'raised')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant || action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'flat')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n </div>\n }\n </div>\n </ng-template>\n\n <!-- Remote pagination stays attached to the collection footer. -->\n @if (isRemoteDataMode() && !(error$ | async)) {\n @if (page$ | async; as ps) {\n @if (total$ | async; as total) {\n @if (config.ui?.showRange === false) {\n <div class=\"list-paginator-total\" aria-live=\"polite\">\n {{ remoteTotalLabel(total) }}\n </div>\n }\n <mat-paginator\n [length]=\"total || 0\"\n [pageIndex]=\"ps.pageNumber || 0\"\n [pageSize]=\"ps.pageSize || config.layout?.pageSize || 10\"\n [pageSizeOptions]=\"listPageSizeOptions()\"\n [selectConfig]=\"paginatorSelectConfig\"\n [showFirstLastButtons]=\"true\"\n [class.hide-range]=\"config.ui?.showRange === false\"\n (page)=\"onPageChange($event)\"\n >\n </mat-paginator>\n }\n }\n }\n </ng-template>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block}.praxis-list-root{container-type:inline-size;--p-list-radius: var(--md-sys-shape-corner-medium, 16px);--p-list-shadow: var(--md-sys-elevation-level2);--p-list-border: 1px solid var(--md-sys-color-outline-variant);--p-list-blur: 10px;--p-list-surface: var(--md-sys-color-surface-container);--p-list-surface-low: var(--md-sys-color-surface);--p-list-surface-high: var( --md-sys-color-surface-container-high, var(--md-sys-color-surface-container) );--p-list-foreground: var(--md-sys-color-on-surface);--p-list-foreground-muted: var(--md-sys-color-on-surface-variant);--p-list-accent: var(--md-sys-color-primary);--p-list-accent-weak: var(--md-sys-color-primary-container);--p-list-item-surface: var( --md-sys-color-surface-container-low, var(--md-sys-color-surface) );--p-list-item-border: 1px solid var(--md-sys-color-outline-variant);--p-list-item-hover-surface: var(--md-sys-color-surface-container-low);--p-list-item-active-surface: var(--md-sys-color-surface-container);--p-list-item-selected-surface: var(--md-sys-color-surface-container);--p-list-grad-from: var(--md-sys-color-primary-container);--p-list-grad-to: var(--md-sys-color-tertiary-container);--p-list-grad-angle: 135deg;--p-list-grad-foreground: var( --md-sys-color-on-primary-container, var(--md-sys-color-on-surface) );--p-list-grad-foreground-muted: color-mix( in srgb, var(--p-list-grad-foreground) 76%, transparent );--p-list-leading-width: 36px;--p-list-trailing-min-width: 140px;--p-list-trailing-max-width: 320px;--p-list-selection-end-gutter: 56px;--p-list-item-gap: 10px;--p-list-item-padding-x: 14px;--p-list-item-padding-y: 10px;--p-list-item-stack-gap: 0px;--p-list-meta-size: .875rem;--p-list-meta-weight: 500;--p-list-chip-height: 22px;--p-list-chip-font-size: 12px;--p-list-trailing-padding-right: 8px;--p-list-action-button-height: 32px;--p-list-tile-minW: 240px;--p-list-tile-gap: 12px;--p-list-tile-padding: 12px;--p-list-tile-radius: 12px;--p-list-tile-media-radius: 12px;--p-list-tile-media-ratio: 1 / 1;--p-list-tile-hover-overlay: .04;--p-list-tile-press-overlay: .08;--p-list-tile-press-scale: .99}.list-assistant{display:flex;justify-content:flex-end;gap:8px;padding:4px 4px 8px}.action-loading{opacity:.65}.action-spinner{width:16px;height:16px;display:inline-flex;align-items:center;justify-content:center}.action-loading .mat-mdc-button-touch-target,.action-loading .mdc-button__label{opacity:.7}.skin-elevated,.skin-outline,.skin-flat,.skin-neumorphism{--p-list-item-surface: var( --mdc-elevated-card-container-color, var(--p-list-surface) );--p-list-item-border: var(--p-list-border)}.skin-elevated .item-card,.skin-outline .item-card,.skin-flat .item-card,.skin-neumorphism .item-card{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:var(--p-list-radius);box-shadow:var(--p-list-shadow);border:var(--p-list-border)}.skin-elevated .item-tile,.skin-outline .item-tile,.skin-flat .item-tile,.skin-neumorphism .item-tile{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));box-shadow:var(--p-list-shadow);border:var(--p-list-border)}.skin-elevated .mat-mdc-list-item .list-item-content,.skin-outline .mat-mdc-list-item .list-item-content,.skin-flat .mat-mdc-list-item .list-item-content,.skin-neumorphism .mat-mdc-list-item .list-item-content{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * .75);box-shadow:var(--p-list-shadow);border:var(--p-list-item-border);color:var(--p-list-foreground)}.skin-outline{--p-list-shadow: none;--p-list-border: 1px solid var(--md-sys-color-outline)}.skin-flat{--p-list-shadow: none;--p-list-border: 1px solid var(--md-sys-color-outline-variant)}.skin-neumorphism{--p-list-shadow: var(--md-sys-elevation-level2);--p-list-border: 1px solid var(--md-sys-color-outline-variant);--p-list-radius: 1.25rem}.skin-pill-soft .item-card,.skin-pill-soft .item-tile{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:var(--md-sys-elevation-level1);border:var(--p-list-border)}.skin-pill-soft .mat-mdc-list-item .list-item-content{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:var(--md-sys-elevation-level1);border:var(--p-list-border);color:var(--p-list-foreground)}.skin-gradient-tile{--p-list-foreground: var(--p-list-grad-foreground);--p-list-foreground-muted: var(--p-list-grad-foreground-muted);--p-list-item-hover-surface: linear-gradient( var(--p-list-grad-angle), var(--p-list-grad-from), var(--p-list-grad-to) );--p-list-item-active-surface: linear-gradient( var(--p-list-grad-angle), var(--p-list-grad-from), var(--p-list-grad-to) );--p-list-item-selected-surface: linear-gradient( var(--p-list-grad-angle), var(--p-list-grad-from), var(--p-list-grad-to) )}.skin-gradient-tile .item-card,.skin-gradient-tile .item-tile{background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));border-radius:var(--p-list-radius);color:var(--p-list-foreground)}.skin-gradient-tile .tile-media{background:color-mix(in srgb,var(--md-sys-color-surface) 74%,transparent);border-color:color-mix(in srgb,var(--p-list-foreground) 22%,transparent)}.skin-gradient-tile .tile-media mat-icon,.skin-gradient-tile .tile-meta,.skin-gradient-tile .tile-trailing,.skin-gradient-tile .feature,.skin-gradient-tile .secondary,.skin-gradient-tile .meta{color:var(--p-list-foreground-muted)}.skin-gradient-tile .tile-status,.skin-gradient-tile [data-rich-node-type=badge] .prx-rich-badge{color:var(--md-sys-color-on-secondary-container, var(--p-list-foreground));background:var(--md-sys-color-secondary-container, rgba(255, 255, 255, .72))}.skin-gradient-tile .mat-mdc-list-item .list-item-content{background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));border-radius:var(--p-list-radius);color:var(--p-list-foreground)}.skin-glass .item-card,.skin-glass .item-tile{background:var(--md-sys-color-surface-container-low);border-radius:var(--p-list-radius);border:1px solid var(--md-sys-color-outline-variant);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur))}.skin-glass .mat-mdc-list-item .list-item-content{background:var(--md-sys-color-surface-container-low);border-radius:var(--p-list-radius);border:1px solid var(--md-sys-color-outline-variant);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur));color:var(--p-list-foreground)}.density-compact .mat-mdc-list-item,.density-compact .item-card{--p-list-item-padding-y: 6px;--p-list-item-padding-x: 10px;--p-list-item-gap: 8px}.density-comfortable .mat-mdc-list-item,.density-comfortable .item-card{--p-list-item-padding-y: 8px;--p-list-item-padding-x: 12px;--p-list-item-gap: 10px}.praxis-list-root.lines-3 .mat-mdc-list-item,.praxis-list-root.lines-3 .mat-mdc-list-option{min-height:68px}.density-compact{--p-list-tile-gap: 10px;--p-list-tile-padding: 10px;--p-list-tile-minW: 188px}.density-comfortable{--p-list-tile-gap: 12px;--p-list-tile-padding: 12px}.item-spacing-none{--p-list-item-stack-gap: 0px}.item-spacing-tight{--p-list-item-stack-gap: 4px;--p-list-tile-gap: 10px}.item-spacing-default{--p-list-item-stack-gap: 6px;--p-list-tile-gap: 12px}.item-spacing-relaxed{--p-list-item-stack-gap: 14px;--p-list-tile-gap: 20px}.list-item-content{box-sizing:border-box;display:grid;grid-template-columns:var(--p-list-leading-width) minmax(0,1fr) minmax(var(--p-list-trailing-min-width),min(34vw,var(--p-list-trailing-max-width)));align-items:center;gap:var(--p-list-item-gap);padding:var(--p-list-item-padding-y) var(--p-list-item-padding-x);width:100%;min-width:0;border-radius:calc(var(--p-list-radius) * .75);overflow:visible}.list-item-content--row-layout{--p-list-row-template-columns: var(--p-list-leading-width) minmax(0, 1fr) minmax(var(--p-list-trailing-min-width), max-content);--p-list-row-gap: var(--p-list-item-gap);--p-list-row-align-items: center;grid-template-columns:var(--p-list-row-template-columns);gap:var(--p-list-row-gap);align-items:var(--p-list-row-align-items)}.list-item-content--row-layout[role=button]{cursor:pointer}.list-item-content--row-layout[role=button]:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.list-row-slot{min-width:0;height:100%;align-self:stretch;display:flex;align-items:var(--p-list-row-slot-align-items, center);gap:8px}.list-item-content--row-layout .list-row-slot>praxis-rich-content,.list-item-content--row-layout .list-row-slot>praxis-list-metric,.list-item-content--row-layout .list-row-slot>praxis-list-compose,.list-item-content--row-layout .list-row-slot>praxis-list-runtime-component{min-width:0}.list-row-slot--leading{justify-content:center}.list-row-slot--primary,.list-row-slot--secondary{min-width:0}.list-row-slot--meta{color:var(--p-list-foreground);font-variant-numeric:tabular-nums}.list-row-slot--trailing{flex-wrap:wrap;justify-content:flex-end}.list-row-slot .primary,.list-row-slot .secondary,.list-row-slot .meta,.list-row-slot .trailing{min-width:0}.list-item-main-action{grid-column:1/3;display:grid;grid-template-columns:var(--p-list-leading-width) minmax(0,1fr);align-items:center;gap:var(--p-list-item-gap);min-width:0;padding:0;border:0;background:transparent;color:inherit;text-align:start;font:inherit;cursor:pointer}.list-item-main-action:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px;border-radius:12px}.list-item-leading{display:flex;align-items:center;justify-content:center;min-height:100%}.leading-placeholder{width:24px;height:24px;display:inline-block}.list-item-text{min-width:0;display:grid;align-content:center;gap:2px}.list-item-trailing{box-sizing:border-box;min-width:0;width:100%;max-width:var(--p-list-trailing-max-width);display:grid;align-self:center;align-items:center;justify-content:end;justify-items:end;align-content:center;gap:4px;text-align:end;padding-right:var(--p-list-trailing-padding-right)}.list-item-trailing>*{align-self:center}.list-item-content--row-layout .list-item-actions{justify-content:flex-end}.list-item-actions{width:max-content;justify-self:end;align-self:center;display:flex;align-items:center;justify-content:flex-end;gap:4px;flex-wrap:wrap;max-width:100%;min-height:var(--p-list-action-button-height)}.list-item-actions .mat-mdc-button-base{min-height:var(--p-list-action-button-height);height:var(--p-list-action-button-height);display:inline-flex;align-items:center}.expand-toggle{color:var(--p-list-foreground-muted)}.mat-mdc-list-item .list-item-content{background:var(--p-list-item-surface);border:var(--p-list-item-border);transition:background .15s ease,box-shadow .15s ease,transform .15s ease}.mat-mdc-list-item,.mat-mdc-list-option{height:auto;align-items:stretch;overflow:visible}.mat-mdc-list-option .mdc-list-item__end{align-self:flex-start;margin-top:var(--p-list-item-padding-y);margin-bottom:0}.mat-mdc-list-option .list-item-content{padding-right:calc(var(--p-list-item-padding-x) + var(--p-list-selection-end-gutter))}.mat-mdc-list-item.mdc-list-item--with-one-line,.mat-mdc-list-item.mdc-list-item--with-two-lines,.mat-mdc-list-item.mdc-list-item--with-three-lines,.mat-mdc-list-item-single-line,.mat-mdc-list-item-two-line,.mat-mdc-list-item-three-line,.mat-mdc-list-option.mdc-list-item--with-one-line,.mat-mdc-list-option.mdc-list-item--with-two-lines,.mat-mdc-list-option.mdc-list-item--with-three-lines{height:auto!important}.mdc-list-item{overflow:visible}.density-compact .mat-mdc-list-item,.density-compact .mat-mdc-list-option{min-height:40px}.density-comfortable .mat-mdc-list-item,.density-comfortable .mat-mdc-list-option{min-height:46px}.praxis-list-root:not(.density-compact):not(.density-comfortable) .mat-mdc-list-item,.praxis-list-root:not(.density-compact):not(.density-comfortable) .mat-mdc-list-option{min-height:48px}.mat-mdc-list-item .mdc-list-item__content,.mat-mdc-list-option .mdc-list-item__content,.mat-mdc-list-item .mdc-list-item__primary-text,.mat-mdc-list-option .mdc-list-item__primary-text{display:block!important;width:100%!important;height:auto!important;padding:0!important;margin:0!important;overflow:visible!important;box-sizing:border-box}.mat-mdc-list-item,.mat-mdc-list-option{padding:0!important;box-sizing:border-box}.praxis-list-root mat-list>mat-list-item:not(:last-child),.praxis-list-root mat-selection-list>mat-list-option:not(:last-child){margin-bottom:var(--p-list-item-stack-gap)}.mat-mdc-list-item:hover .list-item-content,.mat-mdc-list-option:hover .list-item-content{background:var(--p-list-item-hover-surface)}.mat-mdc-list-item:active .list-item-content,.mat-mdc-list-option:active .list-item-content{background:var(--p-list-item-active-surface)}.mat-mdc-list-option.mdc-list-item--selected .list-item-content{background:var(--p-list-item-selected-surface);border-color:var(--md-sys-color-outline-variant)}.mat-mdc-list-option.cdk-keyboard-focused .list-item-content,.mat-mdc-list-option.cdk-focused .list-item-content{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.mat-mdc-list-item:focus-visible .list-item-content{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.praxis-list-root mat-selection-list>.mat-mdc-list-option{border-radius:calc(var(--p-list-radius) * .75);background:var(--p-list-item-surface);border:var(--p-list-item-border);color:var(--p-list-foreground);overflow:hidden;transition:background .15s ease,box-shadow .15s ease,border-color .15s ease}.skin-elevated mat-selection-list>.mat-mdc-list-option,.skin-outline mat-selection-list>.mat-mdc-list-option,.skin-flat mat-selection-list>.mat-mdc-list-option,.skin-neumorphism mat-selection-list>.mat-mdc-list-option{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border:var(--p-list-item-border);box-shadow:var(--p-list-shadow)}.skin-pill-soft mat-selection-list>.mat-mdc-list-option{border-radius:calc(var(--p-list-radius) * 1.3);background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border:var(--p-list-border);box-shadow:var(--md-sys-elevation-level1)}.skin-gradient-tile mat-selection-list>.mat-mdc-list-option{border-radius:var(--p-list-radius);background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));color:var(--p-list-foreground)}.skin-glass mat-selection-list>.mat-mdc-list-option{border-radius:var(--p-list-radius);background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur))}.praxis-list-root mat-selection-list>.mat-mdc-list-option:hover{background:var(--p-list-item-hover-surface)}.praxis-list-root mat-selection-list>.mat-mdc-list-option:active{background:var(--p-list-item-active-surface)}.praxis-list-root mat-selection-list>.mat-mdc-list-option.mdc-list-item--selected{background:var(--p-list-item-selected-surface);border-color:var(--md-sys-color-outline-variant)}.praxis-list-root mat-selection-list>.mat-mdc-list-option .list-item-content{background:transparent;border:0;box-shadow:none;border-radius:0}.praxis-list-root mat-selection-list>.mat-mdc-list-option.cdk-keyboard-focused,.praxis-list-root mat-selection-list>.mat-mdc-list-option.cdk-focused{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.executive-inline-skin{--p-list-leading-width: 56px;--p-list-trailing-min-width: 232px;--p-list-item-padding-x: 14px;--p-list-item-padding-y: 8px;--p-list-item-gap: 10px;--p-list-surface: #f8fafb;--p-list-surface-low: #ffffff;--p-list-item-surface: #ffffff;--p-list-item-hover-surface: #f7fafc;--p-list-item-active-surface: #eef4f8;--p-list-item-selected-surface: #eef4f8;--p-list-foreground: #17324d;--p-list-foreground-muted: #72849a}.executive-inline-skin .list-item-content--row-layout.exec-row-layout{min-height:72px;padding:12px 20px;background:linear-gradient(180deg,#fff,#fbfcfd)!important;border:1px solid #dce5ec!important;border-radius:18px;box-shadow:0 1px 2px #0f223a08,0 4px 14px #0f223a08!important;color:#17324d!important}.executive-inline-skin .mat-mdc-list-item:hover .list-item-content--row-layout.exec-row-layout{background:linear-gradient(180deg,#fff,#f7fafc)!important}.executive-inline-skin .list-item-content--row-layout.exec-row-layout+.item-expansion{margin-left:10px;margin-right:10px}.executive-inline-skin .list-item-content--row-layout.exec-row-layout .list-row-slot{min-height:34px}.executive-inline-skin .list-row-slot--leading{justify-content:center}.executive-inline-skin .list-row-slot--identity{align-items:center;padding-right:4px}.executive-inline-skin .list-row-slot--balance,.executive-inline-skin .list-row-slot--limit,.executive-inline-skin .list-row-slot--risk,.executive-inline-skin .list-row-slot--alerts{justify-content:center;padding-inline:2px}.executive-inline-skin .list-row-slot--owner{justify-content:flex-start;padding-left:0}.executive-inline-skin .list-row-slot--actions{justify-content:flex-end;padding-left:0}.executive-inline-skin .list-row-slot--expand{justify-content:center}.executive-inline-skin .docs-expansion-leading{width:44px;height:44px;display:inline-flex;align-items:center;justify-content:center;border-radius:15px;background:linear-gradient(180deg,#edf4f9,#e6eef5);color:#1b5f86;font-size:1rem;font-weight:800;letter-spacing:.02em;box-shadow:0 1px 3px #1b5f861f;border:1px solid #dbe7f0}.executive-inline-skin .exec-balance-metric,.executive-inline-skin .exec-limit-metric,.executive-inline-skin .exec-risk-metric{min-width:0}.executive-inline-skin .exec-balance-metric .p-list-metric,.executive-inline-skin .exec-limit-metric .p-list-metric,.executive-inline-skin .exec-risk-metric .p-list-metric{gap:5px}.executive-inline-skin .exec-balance-metric .p-list-metric__label,.executive-inline-skin .exec-limit-metric .p-list-metric__label,.executive-inline-skin .exec-risk-metric .p-list-metric__label{font-size:.62rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#8090a1}.executive-inline-skin .exec-balance-metric .p-list-metric__value,.executive-inline-skin .exec-limit-metric .p-list-metric__value{font-size:.96rem;font-weight:800;letter-spacing:-.02em;color:#0d1f34;line-height:1.05}.executive-inline-skin .exec-limit-metric .p-list-metric__caption{font-size:.66rem;font-weight:700;color:#8090a1}.executive-inline-skin .exec-limit-metric .p-list-metric__progress{width:68px;justify-self:center;height:3px}.executive-inline-skin .exec-risk-metric{min-width:84px}.executive-inline-skin .exec-risk-metric .p-list-metric{justify-items:center}.executive-inline-skin .exec-risk-metric .p-list-metric__main{gap:6px}.executive-inline-skin .exec-risk-metric .p-list-metric__value{font-size:1.02rem;font-weight:800;letter-spacing:-.024em;line-height:1}.executive-inline-skin .exec-risk-metric .p-list-metric__progress{width:56px;justify-self:center;height:3px}.executive-inline-skin .exec-risk-metric .p-list-metric__caption{font-size:.7rem;font-weight:700;line-height:1.1}.executive-inline-skin .exec-risk-metric .p-list-metric__caption--below-progress{justify-self:center;margin-top:-1px}.executive-inline-skin .exec-risk-metric .p-list-metric__icon mat-icon{width:15px;height:15px;font-size:15px}.executive-inline-skin .list-item-actions{gap:6px;flex-wrap:nowrap}.executive-inline-skin .list-row-slot--actions .list-item-actions{justify-content:flex-end}.executive-inline-skin .list-item-actions button,.executive-inline-skin .expand-toggle{width:30px;height:30px;min-width:30px;border-radius:12px;color:#6d7f93!important}.executive-inline-skin .list-item-actions button mat-icon,.executive-inline-skin .expand-toggle mat-icon{width:18px;height:18px;font-size:18px}.executive-inline-skin .item-expansion{position:relative;margin-top:-10px;padding:22px 26px;background:linear-gradient(180deg,#fcfdfe,#f5f8fb);border-color:#e4eaf0;border-radius:0 0 22px 22px;box-shadow:inset 0 1px #ffffffdb}.lead-image{position:relative;width:96px;height:72px;border-radius:12px;overflow:hidden}.lead-image img{width:100%;height:100%;object-fit:cover;display:block}.lead-image .lead-badge{position:absolute;left:8px;bottom:8px}.inline-image{position:relative;width:20px;height:20px;border-radius:4px;overflow:hidden;display:inline-block;vertical-align:middle}.inline-image img{width:100%;height:100%;object-fit:cover;display:block}.inline-image .inline-badge{position:absolute;left:2px;bottom:2px}.model-media .lead-image{width:136px;height:96px;border-radius:16px}.model-media .list-item-content{gap:16px}.model-media .item-card{--p-list-leading-width: 136px}.model-hotel .lead-image{width:160px;height:110px;border-radius:18px}.model-hotel .item-card{--p-list-leading-width: 160px}.primary,.secondary{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis}.primary{-webkit-line-clamp:1;font-weight:600;color:var(--p-list-foreground)}.secondary{-webkit-line-clamp:1;color:var(--p-list-foreground-muted)}.lines-3 .tertiary{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.item-tile .primary{-webkit-line-clamp:2;font-size:.95rem;line-height:1.25rem}.item-tile .secondary{-webkit-line-clamp:2;font-size:.8rem;line-height:1.1rem}.tertiary{color:var(--p-list-foreground-muted)}.features{display:flex;flex-wrap:wrap;gap:12px;margin-top:6px;color:inherit}.feature{display:inline-flex;align-items:center;gap:6px}.feature-line{display:block}.feature-progress{display:block;width:88px;height:4px;margin-top:6px;border-radius:999px;background:#dbe5eb;overflow:hidden}.feature-progress__value{display:block;height:100%;border-radius:inherit;background:#2f9b69}.meta{color:var(--p-list-foreground);font-size:var(--p-list-meta-size);font-weight:var(--p-list-meta-weight);font-variant-numeric:tabular-nums;max-width:100%;min-width:0;display:inline-flex;align-items:center;justify-content:flex-end;gap:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.meta>span,.meta praxis-rich-content{min-width:0;display:inline-flex;align-items:center;justify-content:flex-end;gap:4px;line-height:1.15;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.meta .mat-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;line-height:1;vertical-align:middle}.trailing{color:var(--p-list-foreground-muted);margin-left:0;max-width:100%;min-width:0;display:inline-flex;justify-content:flex-end;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.trailing praxis-rich-content{min-width:0}:host ::ng-deep .meta praxis-rich-content .prx-rich-node[data-rich-node-type=badge],:host ::ng-deep .trailing praxis-rich-content .prx-rich-node[data-rich-node-type=badge],:host ::ng-deep .list-row-slot praxis-rich-content .prx-rich-node[data-rich-node-type=badge]{background:transparent;color:inherit}:host ::ng-deep .meta praxis-rich-content .prx-rich-node[data-rich-node-type=badge] .prx-rich-badge,:host ::ng-deep .trailing praxis-rich-content .prx-rich-node[data-rich-node-type=badge] .prx-rich-badge,:host ::ng-deep .list-row-slot praxis-rich-content .prx-rich-node[data-rich-node-type=badge] .prx-rich-badge{min-height:22px;padding:1px 9px;border:1px solid var(--p-list-chip-border-color, transparent);background:var(--p-list-chip-background, var(--md-sys-color-primary-container, #e8def8));color:var(--p-list-chip-color, var(--md-sys-color-on-primary-container, #21005d));line-height:18px}.section-header{font-size:.85rem;color:var(--p-list-foreground-muted);padding:8px 12px}.section-header--setup{margin:8px 12px 12px;padding:14px 16px;border:1px dashed var(--md-sys-color-outline-variant);border-radius:8px;background:color-mix(in srgb,var(--p-list-surface) 92%,var(--md-sys-color-primary) 8%);color:var(--p-list-foreground);font-weight:500}.praxis-list-root mat-list,.praxis-list-root mat-selection-list{display:block;padding-bottom:12px}.praxis-list-root .mat-divider{margin-left:var(--p-list-item-padding-x);margin-right:var(--p-list-item-padding-x)}.item-expansion{margin:0 var(--p-list-item-padding-x) var(--p-list-item-stack-gap);padding:var(--p-list-expansion-padding, 14px 16px);background:var(--md-sys-color-surface-container-low, var(--p-list-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:calc(var(--p-list-radius) * .75)}.item-expansion--detached,.item-expansion--modal{margin-top:var(--p-list-item-stack-gap);background:var(--md-sys-color-surface-container, var(--p-list-surface));box-shadow:var(--md-sys-elevation-level1)}.item-expansion--modal{border-color:var(--md-sys-color-outline);box-shadow:var(--md-sys-elevation-level3)}.item-expansion-grid{display:grid;grid-template-columns:var(--p-list-expansion-columns, repeat(auto-fit, minmax(220px, 1fr)));gap:var(--p-list-expansion-gap, 16px)}.expansion-section{min-width:0;display:grid;gap:10px;align-content:start}.expansion-section-title{font-size:.78rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--p-list-foreground-muted)}.expansion-info-list,.expansion-timeline,.expansion-key-value{display:grid;gap:10px}.expansion-info-item,.expansion-timeline-item{display:grid;gap:4px;padding:10px 12px;border-radius:14px;background:var(--p-list-item-surface);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 72%,transparent 28%)}.expansion-info-title,.expansion-timeline-title{font-size:.92rem;font-weight:600;color:var(--p-list-foreground)}.expansion-info-value,.expansion-timeline-meta,.expansion-timeline-description,.expansion-empty{font-size:.82rem;line-height:1.45;color:var(--p-list-foreground-muted)}.expansion-chip-list{display:flex;flex-wrap:wrap;gap:8px}.expansion-chip{background:var(--p-list-item-surface);border:1px solid var(--md-sys-color-outline-variant)}.expansion-key-value{margin:0}.expansion-key-value-row{display:grid;gap:4px;padding:4px 0 10px;border-bottom:1px solid var(--md-sys-color-outline-variant)}.expansion-key-value-row dt,.expansion-key-value-row dd{margin:0}.expansion-key-value-row dt{font-size:.78rem;font-weight:600;color:var(--p-list-foreground-muted)}.expansion-key-value-row dd{font-size:.9rem;color:var(--p-list-foreground)}.cards-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,max(340px,var(--p-list-tile-minW))),1fr));gap:max(var(--p-list-tile-gap),16px)}.tiles-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(var(--p-list-tile-minW),1fr));gap:var(--p-list-tile-gap)}.item-tile{position:relative;display:flex;flex-direction:column;gap:10px;padding:var(--p-list-tile-padding);border-radius:var(--p-list-tile-radius);background:var(--p-list-surface);border:var(--p-list-border);color:var(--p-list-foreground);cursor:pointer;min-height:160px;transition:box-shadow .16s ease-out,border-color .16s ease-out,transform .12s ease-out,background .16s ease-out}.item-tile:hover{box-shadow:var(--md-sys-elevation-level2);border-color:var(--md-sys-color-outline-variant);background:var(--p-list-item-hover-surface)}.item-tile:active{transform:scale(var(--p-list-tile-press-scale));background:var(--p-list-item-active-surface)}.tile-main-action{display:flex;flex:1;min-width:0;flex-direction:column;gap:10px;color:inherit;cursor:pointer}.tile-main-action:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px;border-radius:calc(var(--p-list-tile-radius) - 4px)}.tile-media{width:100%;aspect-ratio:var(--p-list-tile-media-ratio);border-radius:var(--p-list-tile-media-radius);background:var(--p-list-item-surface);border:1px solid var(--md-sys-color-outline-variant);display:grid;place-items:center;overflow:hidden}.tile-media img{width:100%;height:100%;object-fit:cover;display:block}.tile-media mat-icon{font-size:28px;height:28px;width:28px;color:var(--p-list-foreground-muted)}.tile-body{display:grid;gap:6px;min-width:0}.tile-meta{display:inline-flex;align-items:center;gap:6px;font-size:.75rem;color:var(--p-list-foreground-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tile-trailing{font-size:.75rem;color:var(--p-list-foreground-muted);white-space:nowrap}.tiles-features{display:flex;flex-wrap:wrap;gap:8px;color:inherit}.tile-status{position:absolute;top:10px;right:10px}.tile-actions{position:absolute;top:8px;left:8px;display:flex;gap:4px;opacity:0;pointer-events:none;transform:translateY(-2px);transition:opacity .16s ease-out,transform .16s ease-out}.item-tile:hover .tile-actions,.item-tile:focus-within .tile-actions{opacity:1;pointer-events:auto;transform:translateY(0)}@media(hover:none){.tile-actions{opacity:1;pointer-events:auto;transform:none}}@media(max-width:600px){.tiles-grid{grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}}@media(prefers-reduced-motion:reduce){.item-tile,.tile-actions{transition:none}.item-tile:active{transform:none}.item-card{transition:none}.item-card:active{transform:none}}.item-card{padding:max(var(--p-list-tile-padding),16px);display:flex;flex-direction:column;gap:var(--p-list-item-gap);min-height:220px;position:relative;cursor:pointer;background:var(--p-list-surface);border:var(--p-list-border);border-radius:var(--p-list-tile-radius);box-shadow:var(--p-list-shadow);transition:box-shadow .16s ease-out,border-color .16s ease-out,transform .12s ease-out,background .16s ease-out;--p-list-leading-width: 96px;--p-list-trailing-min-width: 0px}.card-main-action{display:block;width:100%;min-width:0;padding:0;border:0;background:transparent;color:inherit;text-align:start;font:inherit;cursor:pointer}.card-main-action:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px;border-radius:calc(var(--p-list-tile-radius) - 4px)}.item-card .list-item-content{display:block;padding:0;min-width:0;overflow:hidden}.item-card .list-item-leading{position:absolute;top:18px;left:18px;min-height:0;z-index:1}.item-card .leading-placeholder{width:36px;height:36px}.item-card .leading-icon.mat-icon,.item-card .list-item-leading>mat-icon{width:36px;height:36px;line-height:36px;font-size:20px;display:inline-flex;align-items:center;justify-content:center;border-radius:14px;background:linear-gradient(145deg,color-mix(in srgb,var(--md-sys-color-primary) 18%,transparent),color-mix(in srgb,var(--md-sys-color-primary-container) 34%,transparent));color:var(--md-sys-color-primary)}.item-card .list-item-text{display:grid;gap:9px;padding-left:50px;width:100%;min-width:0;box-sizing:border-box}.item-card .primary{padding-right:min(50%,160px);font-size:1rem;font-weight:800;letter-spacing:-.01em;line-height:1.18;min-width:0;overflow-wrap:anywhere}.item-card .secondary{color:var(--p-list-foreground-muted);font-weight:600;line-height:1.35;min-width:0;overflow-wrap:anywhere}.item-card .features{display:flex;flex-direction:column;gap:8px;margin-top:2px}.item-card .feature{display:inline-flex;align-items:start;gap:8px;min-width:0;max-width:100%;overflow-wrap:anywhere}.item-card .feature mat-icon{margin-top:1px;color:var(--p-list-foreground-muted);opacity:.86}.item-card .list-item-trailing{min-width:var(--p-list-trailing-min-width);grid-column:1/-1;align-items:stretch;padding-right:0}.card-detail-strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(148px,1fr));gap:10px;margin-top:16px}.card-detail{min-width:0;padding:11px 12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent);border-radius:14px;background:linear-gradient(145deg,color-mix(in srgb,var(--md-sys-color-primary-container) 22%,transparent),color-mix(in srgb,var(--p-list-surface) 92%,transparent));box-shadow:inset 0 1px #ffffff14}.card-detail__label{display:block;margin-bottom:3px;color:var(--p-list-foreground-muted);font-size:.68rem;font-weight:700;letter-spacing:.08em;line-height:1.1;text-transform:uppercase}.card-detail__value{display:block;min-width:0;color:var(--p-list-foreground);font-size:.94rem;font-weight:800;line-height:1.2;overflow-wrap:normal;word-break:normal}.card-detail__value,.card-detail__value *{white-space:nowrap}.card-detail--balance .card-detail__value{color:var(--md-sys-color-primary)}.card-detail--limit .card-detail__value{color:var(--md-sys-color-error)}.card-detail--risk .card-detail__value{color:var(--md-sys-color-tertiary, var(--p-list-foreground))}.item-card:hover{box-shadow:var(--md-sys-elevation-level3);border-color:var(--md-sys-color-outline-variant);background:var(--p-list-item-hover-surface)}.item-card:active{transform:scale(var(--p-list-tile-press-scale));background:var(--p-list-item-active-surface)}.card-actions{display:flex;align-items:center;justify-content:flex-end;gap:4px;padding:6px 8px 4px;border-top:1px solid var(--md-sys-color-outline-variant);margin-top:auto}.status-overlay{position:absolute;top:14px;right:14px;max-width:min(42%,148px);overflow:hidden;pointer-events:none}.status-overlay .mat-mdc-chip{pointer-events:auto;width:100%;min-width:0;max-width:100%}.status-overlay .mdc-evolution-chip__text-label,.status-overlay .mat-mdc-chip-action-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.leading-icon.mat-icon{width:36px;height:36px;line-height:36px;font-size:20px;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.mat-mdc-chip{--mdc-chip-container-height: var(--p-list-chip-height);font-size:var(--p-list-chip-font-size)}.meta .mat-icon{font-size:18px;height:18px;width:18px}.features{overflow:hidden}@keyframes shimmer{0%{background-position:-468px 0}to{background-position:468px 0}}.skeleton{animation-duration:1.2s;animation-fill-mode:forwards;animation-iteration-count:infinite;animation-name:shimmer;animation-timing-function:linear;background:linear-gradient(to right,var(--md-sys-color-surface-container-low) 8%,var(--md-sys-color-surface-container) 18%,var(--md-sys-color-surface-container-low) 33%);background-size:800px 104px;position:relative}.skeleton-avatar{width:32px;height:32px;border-radius:50%}.skeleton-line{height:10px;margin:6px 0;border-radius:6px}.skeleton-chip{width:48px;height:18px;border-radius:999px}.chip-outlined.mat-mdc-chip{background:transparent!important;border:1px solid currentColor}.w-60{width:60%}.w-40{width:40%}.list-collection-toolbar{display:flex;align-items:center;gap:12px;padding:12px;margin-bottom:8px;color:var(--p-list-foreground);background:var(--p-list-surface-low);border:var(--p-list-border);border-radius:calc(var(--p-list-radius) * .75)}.list-toolbar-search{flex:1 1 320px;min-width:220px;max-width:440px}.list-toolbar-commands{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:8px;margin-inline-start:auto}.list-toolbar-commands button{border-radius:8px}.list-toolbar-commands button .mat-icon{margin-right:6px}.list-toolbar-sort{width:200px}.list-remote-error{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:12px;margin:8px 4px 0;padding:16px;color:var(--md-sys-color-on-error-container);background:var(--md-sys-color-error-container);border-radius:calc(var(--p-list-radius) * .75)}.list-remote-error__copy{display:flex;flex-direction:column;gap:4px;min-width:0}.list-remote-error__copy span{color:color-mix(in srgb,var(--md-sys-color-on-error-container) 82%,transparent);line-height:1.45}.mat-mdc-paginator{margin-top:14px;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--p-list-surface-low);color:var(--p-list-foreground)}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-container{min-height:40px;padding:4px 8px;gap:6px}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-page-size-select{margin-left:8px;width:92px}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-page-size-select .mat-mdc-text-field-wrapper{--mdc-outlined-text-field-container-color: color-mix( in srgb, var(--p-list-surface-low) 92%, var(--md-sys-color-on-surface) 8% );--mdc-outlined-text-field-input-text-color: var(--p-list-foreground);min-height:40px;padding-inline:12px 8px;background:color-mix(in srgb,var(--p-list-surface-low) 92%,var(--md-sys-color-on-surface) 8%)!important;background-color:color-mix(in srgb,var(--p-list-surface-low) 92%,var(--md-sys-color-on-surface) 8%)!important;color:var(--p-list-foreground)!important}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-page-size-select .mat-mdc-form-field-infix{min-height:40px;padding-block:8px;width:auto}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-page-size-select .mat-mdc-select-trigger{min-height:24px;padding-right:2px;color:var(--p-list-foreground)}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-range-label,:host ::ng-deep .mat-mdc-paginator .mat-mdc-select-value-text{font-size:12px;color:var(--p-list-foreground-muted)}.list-paginator-total{margin-top:14px;padding:4px 8px 0;font-size:12px;color:var(--p-list-foreground-muted)}:host ::ng-deep .mat-mdc-paginator.hide-range .mat-mdc-paginator-range-label{display:none}::ng-deep .praxis-list-paginator-select-panel.mat-mdc-select-panel{--mat-select-panel-background-color: var( --md-sys-color-surface-container, #fff );background:var(--md-sys-color-surface-container, #fff)!important;background-color:var(--md-sys-color-surface-container, #fff)!important;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #d7dbe5) 70%,transparent);box-shadow:0 12px 28px color-mix(in srgb,var(--md-sys-color-shadow, #000) 28%,transparent)}::ng-deep .praxis-list-paginator-select-panel .mat-mdc-option{--mat-option-label-text-color: var(--md-sys-color-on-surface, #111827);color:var(--md-sys-color-on-surface, #111827)!important}::ng-deep .praxis-list-paginator-select-panel .mat-mdc-option:hover:not(.mdc-list-item--disabled),::ng-deep .praxis-list-paginator-select-panel .mat-mdc-option.mat-mdc-option-active{--mat-option-hover-state-layer-color: color-mix( in srgb, var(--md-sys-color-primary, #2f7cff) 14%, transparent );background:color-mix(in srgb,var(--md-sys-color-primary, #2f7cff) 14%,transparent)!important;color:var(--md-sys-color-on-surface, #111827)!important}.muted{color:var(--p-list-foreground-muted)}@container (max-width: 720px){.list-item-content{grid-template-columns:var(--p-list-leading-width) minmax(0,1fr)}.list-item-content--row-layout{grid-template-columns:var(--p-list-leading-width) minmax(0,1fr) max-content;grid-auto-flow:row;align-items:start}.list-item-content--row-layout .list-row-slot--leading{grid-column:1;grid-row:1}.list-item-content--row-layout .list-row-slot--primary,.list-item-content--row-layout .list-row-slot--identity{grid-column:2/-1;grid-row:1}.list-item-content--row-layout .list-row-slot--secondary{grid-column:2/-1;grid-row:2}.list-item-content--row-layout :is(.list-row-slot--primary,.list-row-slot--secondary){white-space:normal;overflow-wrap:anywhere}.list-item-content--row-layout .list-row-slot--meta{grid-column:2;grid-row:3;justify-content:flex-start}.list-item-content--row-layout .list-row-slot--trailing{grid-column:3;grid-row:3;justify-content:flex-end}.list-item-content--row-layout :is(.list-row-slot--balance,.list-row-slot--limit,.list-row-slot--risk,.list-row-slot--alerts,.list-row-slot--owner,.list-row-slot--actions,.list-row-slot--expand){grid-column:1/-1}.list-item-trailing{grid-column:1/-1;display:flex;flex-wrap:wrap;justify-content:flex-start;padding-right:0;align-items:center;text-align:start}.list-item-trailing .list-item-actions{margin-inline-start:auto}.item-expansion{margin-left:0;margin-right:0}.list-collection-toolbar{flex-direction:column;align-items:stretch}.list-toolbar-search,.list-toolbar-sort{flex:0 0 auto;width:100%;max-width:none;min-width:0}.list-toolbar-commands{width:100%;margin-inline-start:0}.list-toolbar-sort{flex:1 1 100%}.list-toolbar-commands button{flex:0 0 auto}.list-remote-error{grid-template-columns:auto minmax(0,1fr)}.list-remote-error button{grid-column:1/-1;justify-self:stretch}}@media(max-width:720px){.list-item-content{grid-template-columns:var(--p-list-leading-width) minmax(0,1fr)}.list-item-content--row-layout{grid-template-columns:var(--p-list-leading-width) minmax(0,1fr) max-content;grid-auto-flow:row;align-items:start}.list-item-content--row-layout .list-row-slot--leading{grid-column:1;grid-row:1}.list-item-content--row-layout .list-row-slot--primary,.list-item-content--row-layout .list-row-slot--identity{grid-column:2/-1;grid-row:1}.list-item-content--row-layout .list-row-slot--secondary{grid-column:2/-1;grid-row:2}.list-item-content--row-layout :is(.list-row-slot--primary,.list-row-slot--secondary){white-space:normal;overflow-wrap:anywhere}.list-item-content--row-layout .list-row-slot--meta{grid-column:2;grid-row:3;justify-content:flex-start}.list-item-content--row-layout .list-row-slot--trailing{grid-column:3;grid-row:3;justify-content:flex-end}.list-item-content--row-layout :is(.list-row-slot--balance,.list-row-slot--limit,.list-row-slot--risk,.list-row-slot--alerts,.list-row-slot--owner,.list-row-slot--actions,.list-row-slot--expand){grid-column:1/-1}.list-item-trailing{grid-column:1/-1;display:flex;flex-wrap:wrap;justify-content:flex-start;padding-right:0;align-items:center;text-align:start}.list-item-trailing .list-item-actions{margin-inline-start:auto}.item-expansion{margin-left:0;margin-right:0}.list-collection-toolbar{flex-direction:column;align-items:stretch}.list-toolbar-search,.list-toolbar-sort{flex:0 0 auto;width:100%;max-width:none;min-width:0}.list-toolbar-commands{width:100%;margin-inline-start:0}.list-toolbar-sort{flex:1 1 100%}.list-toolbar-commands button{flex:0 0 auto}.list-remote-error{grid-template-columns:auto minmax(0,1fr)}.list-remote-error button{grid-column:1/-1;justify-self:stretch}}\n"] }]
|
|
15684
|
+
], template: "<div\n class=\"praxis-list-root\"\n [ngClass]=\"skinClasses\"\n [attr.data-skin-scope]=\"skinScopeId\"\n [attr.aria-label]=\"config.a11y?.ariaLabel || null\"\n [attr.aria-labelledby]=\"config.a11y?.ariaLabelledBy || null\"\n>\n @if (inlineCss) {\n <style [attr.nonce]=\"cspNonce || null\" [textContent]=\"inlineCss\"></style>\n }\n\n @if (enableCustomization) {\n <div class=\"list-assistant\">\n <button\n mat-mini-fab\n type=\"button\"\n color=\"primary\"\n (click)=\"openConfigEditor()\"\n [matTooltip]=\"configEditorLabel()\"\n [attr.aria-label]=\"configEditorLabel()\"\n data-testid=\"praxis-list-open-config-editor\"\n >\n <mat-icon [praxisIcon]=\"'edit'\"></mat-icon>\n </button>\n <button\n mat-mini-fab\n type=\"button\"\n color=\"primary\"\n (click)=\"openAiAssistant()\"\n matTooltip=\"Copiloto semantico Praxis\"\n aria-label=\"Abrir copiloto semantico Praxis da lista\"\n data-testid=\"praxis-list-ai-assistant-trigger\"\n >\n <mat-icon [praxisIcon]=\"'auto_awesome'\"></mat-icon>\n </button>\n </div>\n }\n\n @if (aiAssistantOpen && aiAssistantViewState) {\n <praxis-ai-assistant-shell\n [labels]=\"aiAssistantLabels\"\n [mode]=\"aiAssistantViewState.mode\"\n [state]=\"aiAssistantViewState.state\"\n [contextItems]=\"aiAssistantViewState.contextItems\"\n [attachments]=\"aiAssistantViewState.attachments\"\n [messages]=\"aiAssistantViewState.messages\"\n [quickReplies]=\"aiAssistantViewState.quickReplies\"\n [prompt]=\"aiAssistantPrompt\"\n [statusText]=\"aiAssistantViewState.statusText\"\n [errorText]=\"aiAssistantViewState.errorText\"\n [busy]=\"\n aiAssistantViewState.state === 'processing' ||\n aiAssistantViewState.state === 'applying'\n \"\n [canApply]=\"aiAssistantViewState.canApply\"\n [layout]=\"aiAssistantLayout\"\n testIdPrefix=\"praxis-list-ai-assistant\"\n panelTestId=\"praxis-list-ai-assistant-panel\"\n submitTestId=\"praxis-list-ai-assistant-submit\"\n applyTestId=\"praxis-list-ai-assistant-apply\"\n (promptChange)=\"onAiAssistantPromptChange($event)\"\n (submitPrompt)=\"onAiAssistantSubmit($event)\"\n (apply)=\"onAiAssistantApply()\"\n (retryTurn)=\"onAiAssistantRetry()\"\n (cancelTurn)=\"onAiAssistantCancel()\"\n (quickReply)=\"onAiAssistantQuickReply($event)\"\n (editMessage)=\"onAiAssistantEditMessage($event)\"\n (resendMessage)=\"onAiAssistantResendMessage($event)\"\n (layoutChange)=\"onAiAssistantLayoutChange($event)\"\n (close)=\"closeAiAssistant()\"\n ></praxis-ai-assistant-shell>\n }\n\n <!-- Skeleton while loading -->\n @if ((loading$ | async) && hasSkeleton()) {\n @if (isListVariant()) {\n <mat-list>\n @for (_ of skeletonItems(); track $index; let i = $index) {\n <mat-list-item>\n <div class=\"list-item-content\">\n <div class=\"list-item-leading\">\n <div class=\"skeleton skeleton-avatar\"></div>\n </div>\n <div class=\"list-item-text\">\n <div class=\"skeleton skeleton-line w-60\"></div>\n @if (layoutLines > 1) {\n <div class=\"skeleton skeleton-line w-40\"></div>\n }\n </div>\n <div class=\"list-item-trailing\">\n <div class=\"skeleton skeleton-chip\"></div>\n </div>\n </div>\n </mat-list-item>\n }\n </mat-list>\n } @else {\n <div class=\"cards-grid\">\n @for (_ of skeletonItems(); track $index; let i = $index) {\n <div class=\"item-card\">\n <div class=\"list-item-content\">\n <div class=\"list-item-leading\">\n <div class=\"skeleton skeleton-avatar\"></div>\n </div>\n <div class=\"list-item-text\">\n <div class=\"skeleton skeleton-line w-60\"></div>\n @if (layoutLines > 1) {\n <div class=\"skeleton skeleton-line w-40\"></div>\n }\n </div>\n <div class=\"list-item-trailing\">\n <div class=\"skeleton skeleton-chip\"></div>\n </div>\n </div>\n </div>\n }\n </div>\n }\n } @else {\n <ng-container *ngTemplateOutlet=\"notLoading\"></ng-container>\n }\n\n <ng-template #notLoading>\n @if (\n config.export?.enabled ||\n (isRemoteDataMode() &&\n (config.ui?.showSort ||\n (config.ui?.showSearch && config.ui?.searchField)))\n ) {\n <div\n class=\"list-collection-toolbar\"\n role=\"group\"\n [attr.aria-label]=\"t('toolbar.label', 'Controles da lista')\"\n >\n @if (\n isRemoteDataMode() && config.ui?.showSearch && config.ui?.searchField\n ) {\n <mat-form-field\n class=\"list-toolbar-search\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n >\n <mat-label>{{\n config.ui?.searchPlaceholder || t(\"toolbar.search\", \"Buscar\")\n }}</mat-label>\n <mat-icon matPrefix [praxisIcon]=\"'search'\"></mat-icon>\n <input\n matInput\n type=\"search\"\n (input)=\"onSearchInput($any($event.target).value)\"\n />\n </mat-form-field>\n }\n\n <div class=\"list-toolbar-commands\">\n @if (isRemoteDataMode() && config.ui?.showSort) {\n <mat-form-field\n class=\"list-toolbar-sort\"\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n >\n <mat-label>{{ t(\"toolbar.sort\", \"Ordenar por\") }}</mat-label>\n @let activeSort = activeSortValue((page$ | async)?.sort);\n <mat-select\n [value]=\"activeSort\"\n (selectionChange)=\"onSortChange($event.value)\"\n >\n @if (activeSort && !hasSortOption(activeSort)) {\n <mat-option [value]=\"activeSort\">{{ activeSort }}</mat-option>\n }\n @for (op of config.ui?.sortOptions || []; track $index) {\n <mat-option [value]=\"sortOptionValue(op)\">{{\n sortOptionLabel(op)\n }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n\n @if (config.export?.enabled) {\n <button\n mat-stroked-button\n type=\"button\"\n [matMenuTriggerFor]=\"listExportMenu\"\n [attr.aria-label]=\"t('export.button', 'Exportar')\"\n [disabled]=\"exportBusy\"\n [attr.aria-busy]=\"exportBusy\"\n >\n <mat-icon [praxisIcon]=\"'download'\"></mat-icon>\n {{ t(\"export.button\", \"Exportar\") }}\n </button>\n <span class=\"cdk-visually-hidden\" aria-live=\"polite\">\n {{ exportStatusMessage }}\n </span>\n <mat-menu #listExportMenu=\"matMenu\">\n @for (format of listExportFormats(); track format) {\n <button\n mat-menu-item\n type=\"button\"\n (click)=\"onExportAction(format)\"\n >\n <mat-icon [praxisIcon]=\"exportIcon(format)\"></mat-icon>\n {{ format.toUpperCase() }}\n </button>\n }\n </mat-menu>\n }\n </div>\n </div>\n }\n\n @if (error$ | async; as remoteError) {\n <div class=\"list-remote-error\" role=\"alert\">\n <mat-icon [praxisIcon]=\"'error_outline'\"></mat-icon>\n <div class=\"list-remote-error__copy\">\n <strong>{{\n t(\"remoteError.title\", \"N\u00E3o foi poss\u00EDvel carregar a lista\")\n }}</strong>\n <span>\n {{\n remoteError.filtered\n ? t(\n \"remoteError.filteredDescription\",\n \"Os filtros permanecem aplicados. Nenhum dado sem filtro foi exibido.\"\n )\n : t(\n \"remoteError.description\",\n \"Verifique a conex\u00E3o e tente novamente.\"\n )\n }}\n </span>\n </div>\n <button mat-stroked-button type=\"button\" (click)=\"retryRemoteData()\">\n {{ t(\"remoteError.retry\", \"Tentar novamente\") }}\n </button>\n </div>\n }\n\n <!-- Empty state -->\n @if (items$ | async; as all) {\n @if (all.length === 0 && !(error$ | async)) {\n <div\n class=\"section-header\"\n [class.section-header--setup]=\"isResourceConnectionMissing()\"\n >\n @if (emptyStateTemplate(); as empty) {\n @if (\n simpleRichContentNodes(empty, {\n imageAlt: config.templating?.emptyState?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content [nodes]=\"richNodes\"></praxis-rich-content>\n } @else {\n @switch (empty.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"empty.value\"\n [ngClass]=\"empty.class\"\n [color]=\"\n isThemeColor(empty.color) ? empty.color : undefined\n \"\n [style.cssText]=\"\n (empty.style ? empty.style + ';' : '') +\n iconStyle(empty.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <span\n class=\"inline-image\"\n [ngClass]=\"empty.class\"\n [style.cssText]=\"empty.style\"\n >\n <img\n [src]=\"empty.value\"\n [alt]=\"config.templating?.emptyState?.imageAlt || ''\"\n />\n @if (empty.badge?.value) {\n <mat-chip\n class=\"inline-badge\"\n [color]=\"\n isThemeColor(empty.badge?.color)\n ? empty.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (empty.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(empty.badge?.color, empty.badge?.variant)\n \"\n >{{ empty.badge?.value }}</mat-chip\n >\n }\n </span>\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(empty.color) ? empty.color : undefined\n \"\n [ngClass]=\"[\n empty.class || '',\n (empty.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : '',\n ]\"\n [style.cssText]=\"\n chipStyle(empty.color, empty.variant) +\n (empty.style ? ';' + empty.style : '')\n \"\n >{{ empty.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n <span [ngClass]=\"empty.class\" [style.cssText]=\"empty.style\">\n @for (\n _ of ratingRange(empty);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, empty.value)\"\n [color]=\"ratingThemeColor(empty)\"\n [style.cssText]=\"ratingIconStyle(empty)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"empty.class\"\n [style.cssText]=\"empty.style\"\n [innerHTML]=\"empty.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"empty\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose [compose]=\"empty\"></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"empty\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span [ngClass]=\"empty.class\" [style.cssText]=\"empty.style\">{{\n empty.value\n }}</span>\n }\n }\n }\n }\n </div>\n }\n }\n\n <ng-template\n #rowLayoutNode\n let-node\n let-slot=\"slot\"\n let-imageAlt=\"imageAlt\"\n >\n @if (\n simpleRichContentNodes(node, { slot: slot, imageAlt: imageAlt });\n as richNodes\n ) {\n <praxis-rich-content [nodes]=\"richNodes\"></praxis-rich-content>\n } @else {\n @switch (node?.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"node?.value\"\n [ngClass]=\"node?.class\"\n [color]=\"isThemeColor(node?.color) ? node?.color : undefined\"\n [style.cssText]=\"\n (node?.style || '' ? node.style + ';' : '') +\n iconStyle(node?.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <span\n class=\"inline-image\"\n [ngClass]=\"node?.class\"\n [style.cssText]=\"node?.style\"\n >\n <img\n [src]=\"node?.value\"\n [alt]=\"node?.imageAlt || imageAlt || ''\"\n />\n @if (node?.badge?.value) {\n <mat-chip\n class=\"inline-badge\"\n [color]=\"\n isThemeColor(node?.badge?.color)\n ? node?.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (node?.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(node?.badge?.color, node?.badge?.variant)\n \"\n >\n {{ node?.badge?.value }}\n </mat-chip>\n }\n </span>\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"isThemeColor(node?.color) ? node?.color : undefined\"\n [ngClass]=\"[\n node?.class || '',\n (node?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : '',\n ]\"\n [style.cssText]=\"\n chipStyle(node?.color, node?.variant) +\n (node?.style ? ';' + node.style : '')\n \"\n >\n {{ node?.value }}\n </mat-chip>\n }\n @case (\"rating\") {\n <span [ngClass]=\"node?.class\" [style.cssText]=\"node?.style\">\n @for (_ of ratingRange(node); track $index; let idx = $index) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, node?.value)\"\n [color]=\"ratingThemeColor(node)\"\n [style.cssText]=\"ratingIconStyle(node)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"node?.class\"\n [style.cssText]=\"node?.style\"\n [innerHTML]=\"node?.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"node\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose [compose]=\"node\"></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"node\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span [ngClass]=\"node?.class\" [style.cssText]=\"node?.style\">\n @if (\n slot === \"meta\" && config.templating?.metaPrefixIcon;\n as mpi\n ) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n {{ node?.value }}\n </span>\n }\n }\n }\n </ng-template>\n\n <ng-template #featureContent let-item let-feature=\"feature\">\n @if (featureTemplateNode(item, feature); as featureNode) {\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: featureNode,\n slot: 'feature',\n imageAlt: feature?.imageAlt || '',\n }\n \"\n ></ng-container>\n } @else if (featureRichContentNodes(item, feature); as featureNodes) {\n <praxis-rich-content [nodes]=\"featureNodes\"></praxis-rich-content>\n } @else {\n @if (feature.icon && featuresMode() !== \"labels-only\") {\n <mat-icon [praxisIcon]=\"feature.icon\"></mat-icon>\n }\n @if (featuresMode() !== \"icons-only\") {\n <span [ngClass]=\"feature.class\" [style.cssText]=\"feature.style\">\n @for (line of featureLabelLines(item, feature.expr); track $index) {\n <span class=\"feature-line\">{{ line }}</span>\n }\n </span>\n }\n }\n @if (\n featureProgressPercent(item, feature.expr, feature.class);\n as progress\n ) {\n <span class=\"feature-progress\" aria-hidden=\"true\">\n <span\n class=\"feature-progress__value\"\n [style.width.%]=\"progress\"\n ></span>\n </span>\n }\n </ng-template>\n\n <ng-template\n #listRowLayout\n let-item\n let-index=\"index\"\n let-sectionKey=\"sectionKey\"\n let-clickable=\"clickable\"\n >\n <div\n class=\"list-item-content\"\n [ngClass]=\"rowLayoutItemClass(item)\"\n [attr.style]=\"rowLayoutItemStyle(item)\"\n >\n @if (clickable) {\n <button\n type=\"button\"\n class=\"list-row-main-action\"\n [attr.aria-label]=\"itemAriaLabel(item)\"\n [attr.aria-expanded]=\"\n expansionOwnedByRow() ? isExpanded(item, index) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByRow() ? expandRegionId(item, index) : null\n \"\n (click)=\"onRowActivate(item, index, sectionKey)\"\n ></button>\n }\n @for (\n column of rowLayoutColumns();\n track rowLayoutTrackColumn($index, column)\n ) {\n <div\n [attr.data-row-slot]=\"column.slot\"\n [ngClass]=\"rowLayoutColumnClass(column)\"\n [attr.style]=\"rowLayoutColumnStyle(column)\"\n >\n @if (column.slot === \"leading\") {\n @if (rowLayoutSlot(item, column.slot); as lead) {\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: lead,\n slot: column.slot,\n imageAlt: rowLayoutImageAlt(column.slot),\n }\n \"\n ></ng-container>\n } @else {\n <span class=\"leading-placeholder\"></span>\n }\n } @else if (column.slot === \"trailing\") {\n @if (rowLayoutSlot(item, column.slot); as trailingNode) {\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: trailingNode,\n slot: column.slot,\n imageAlt: rowLayoutImageAlt(column.slot),\n }\n \"\n ></ng-container>\n }\n @if (showExpandIcon(\"trailing\")) {\n <button\n type=\"button\"\n [praxisIconButton]=\"\n isExpanded(item, index) ? 'expand_less' : 'expand_more'\n \"\n size=\"compact\"\n class=\"expand-toggle\"\n [attr.aria-label]=\"expandToggleAriaLabel(item, index)\"\n [attr.aria-expanded]=\"\n expansionOwnedByIcon() ? isExpanded(item, index) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByIcon() ? expandRegionId(item, index) : null\n \"\n (click)=\"onExpandToggle($event, item, index)\"\n ></button>\n }\n @if ((visibleActions(item, \"trailing\") || []).length) {\n <div\n class=\"list-item-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(item, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, item, index)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"onActionClick($event, action.id, item, index)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'stroked')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'raised')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant || action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'flat')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n } @else if (column.slot === \"actions\") {\n @if ((visibleActions(item, \"actions\") || []).length) {\n <div\n class=\"list-item-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(item, \"actions\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, item, index)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"onActionClick($event, action.id, item, index)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'stroked')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'raised')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant || action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'flat')\"\n (click)=\"\n onActionClick($event, action.id, item, index)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, item, index)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n index\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, index)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n } @else if (column.slot === \"expand\") {\n @if (showExpandIcon(\"expand\")) {\n <button\n type=\"button\"\n [praxisIconButton]=\"\n isExpanded(item, index) ? 'expand_less' : 'expand_more'\n \"\n size=\"compact\"\n class=\"expand-toggle\"\n [attr.aria-label]=\"expandToggleAriaLabel(item, index)\"\n [attr.aria-expanded]=\"\n expansionOwnedByIcon() ? isExpanded(item, index) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByIcon() ? expandRegionId(item, index) : null\n \"\n (click)=\"onExpandToggle($event, item, index)\"\n ></button>\n }\n } @else if (rowLayoutSlot(item, column.slot); as slotNode) {\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: slotNode,\n slot: column.slot,\n imageAlt: rowLayoutImageAlt(column.slot),\n }\n \"\n ></ng-container>\n }\n </div>\n }\n </div>\n </ng-template>\n\n <!-- List variant -->\n @if (isListVariant()) {\n <!-- Selection and row actions share one neutral list shell so every\n interactive affordance remains a sibling keyboard target. -->\n <ng-container *ngTemplateOutlet=\"readList\"></ng-container>\n\n <!-- Unified list surface -->\n <ng-template #readList>\n <mat-list\n role=\"list\"\n [attr.aria-label]=\"\n isSelectionEnabled()\n ? selectionListAriaLabel()\n : config.a11y?.ariaLabel || null\n \"\n [attr.aria-labelledby]=\"config.a11y?.ariaLabelledBy || null\"\n >\n @for (\n section of sections$ | async;\n track trackBySection($index, section);\n let sidx = $index\n ) {\n @if (section.key) {\n <div class=\"section-header mat-subheader\">\n @if (sectionHeaderTemplate(section.key); as sh) {\n @if (\n simpleRichContentNodes(sh, {\n imageAlt:\n config.templating?.sectionHeader?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (sh.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"sh.value\"\n [ngClass]=\"sh.class\"\n [color]=\"\n isThemeColor(sh.color) ? sh.color : undefined\n \"\n [style.cssText]=\"\n (sh.style ? sh.style + ';' : '') +\n iconStyle(sh.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <span\n class=\"inline-image\"\n [ngClass]=\"sh.class\"\n [style.cssText]=\"sh.style\"\n >\n <img\n [src]=\"sh.value\"\n [alt]=\"\n config.templating?.sectionHeader?.imageAlt || ''\n \"\n />\n @if (sh.badge?.value) {\n <mat-chip\n class=\"inline-badge\"\n [color]=\"\n isThemeColor(sh.badge?.color)\n ? sh.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (sh.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(sh.badge?.color, sh.badge?.variant)\n \"\n >{{ sh.badge?.value }}</mat-chip\n >\n }\n </span>\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(sh.color) ? sh.color : undefined\n \"\n [ngClass]=\"[\n sh.class || '',\n (sh.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : '',\n ]\"\n [style.cssText]=\"\n chipStyle(sh.color, sh.variant) +\n (sh.style ? ';' + sh.style : '')\n \"\n >{{ sh.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n <span [ngClass]=\"sh.class\" [style.cssText]=\"sh.style\">\n @for (\n _ of ratingRange(sh);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, sh.value)\"\n [color]=\"ratingThemeColor(sh)\"\n [style.cssText]=\"ratingIconStyle(sh)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"sh.class\"\n [style.cssText]=\"sh.style\"\n [innerHTML]=\"sh.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"sh\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"sh\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"sh\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span [ngClass]=\"sh.class\" [style.cssText]=\"sh.style\">{{\n sh.value\n }}</span>\n }\n }\n }\n }\n </div>\n }\n @for (\n item of section.items;\n track trackByItem($index, item);\n let i = $index\n ) {\n <mat-list-item\n role=\"listitem\"\n [attr.data-item-status]=\"item?.status || null\"\n >\n <div\n class=\"list-row-shell\"\n [class.list-row-shell--selectable]=\"isSelectionEnabled()\"\n [class.list-row-shell--selected]=\"isItemSelected(item)\"\n >\n @if (hasRowLayoutGrid()) {\n <ng-container\n *ngTemplateOutlet=\"\n listRowLayout;\n context: {\n $implicit: item,\n index: i,\n sectionKey: section.key || undefined,\n clickable: true,\n }\n \"\n ></ng-container>\n } @else {\n <div\n class=\"list-item-content\"\n [ngClass]=\"itemRuleClass(item)\"\n [attr.style]=\"itemRuleStyle(item)\"\n >\n <button\n type=\"button\"\n class=\"list-item-main-action\"\n [attr.aria-label]=\"itemAriaLabel(item)\"\n [attr.aria-expanded]=\"\n expansionOwnedByRow() ? isExpanded(item, i) : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByRow() ? expandRegionId(item, i) : null\n \"\n (click)=\"\n onRowActivate(item, i, section.key || undefined)\n \"\n >\n <div class=\"list-item-leading\">\n @if (leading(item); as lead) {\n @if (\n simpleRichContentNodes(lead, {\n imageAlt:\n config.templating?.leading?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"lead.value\"\n [ngClass]=\"lead.class\"\n [color]=\"\n isThemeColor(lead.color)\n ? lead.color\n : undefined\n \"\n [style.cssText]=\"\n (lead.style ? lead.style + ';' : '') +\n iconStyle(lead.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >\n <img\n [src]=\"lead.value\"\n [alt]=\"\n config.templating?.leading?.imageAlt ||\n ''\n \"\n />\n @if (lead.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(lead.badge?.color)\n ? lead.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (lead.badge?.variant || 'filled') ===\n 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n lead.badge?.color,\n lead.badge?.variant\n )\n \"\n >{{ lead.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"text\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(lead.color)\n ? lead.color\n : undefined\n \"\n [ngClass]=\"\n (lead.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(lead.color, lead.variant)\n \"\n >{{ lead.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(lead);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, lead.value)\"\n [color]=\"ratingThemeColor(lead)\"\n [style.cssText]=\"ratingIconStyle(lead)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n [innerHTML]=\"lead.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"lead\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"lead\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"lead\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n }\n }\n } @else {\n <span class=\"leading-placeholder\"></span>\n }\n </div>\n <div class=\"list-item-text\">\n <div\n class=\"primary\"\n [ngClass]=\"primary(item)?.class\"\n [style.cssText]=\"primary(item)?.style\"\n >\n @if (\n simpleRichContentNodes(primary(item));\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (primary(item)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"primary(item)\"\n ></praxis-list-metric>\n } @else if (primary(item)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"primary(item)\"\n ></praxis-list-compose>\n } @else if (primary(item)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"primary(item)\"\n ></praxis-list-runtime-component>\n } @else if (primary(item)?.type === \"html\") {\n <span [innerHTML]=\"primary(item)?.value\"></span>\n } @else {\n {{ primary(item)?.value }}\n }\n </div>\n @if (layoutLines > 1) {\n <div\n class=\"secondary\"\n [ngClass]=\"secondary(item)?.class\"\n [style.cssText]=\"secondary(item)?.style\"\n >\n @if (\n simpleRichContentNodes(secondary(item));\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (\n item.segmentVariant &&\n item.segmentLabel &&\n item.accountType &&\n item.since\n ) {\n <!-- Component-based rendering for executive items -->\n <span class=\"exec-subline\">\n <praxis-executive-badge\n [variant]=\"item.segmentVariant\"\n [label]=\"item.segmentLabel\"\n ></praxis-executive-badge>\n <span class=\"exec-subcopy\">{{\n item.accountType\n }}</span>\n <span class=\"exec-subseparator\">\u00B7</span>\n <span class=\"exec-subcopy\"\n >Desde {{ item.since }}</span\n >\n </span>\n } @else if (secondary(item)?.type === \"html\") {\n <span\n [innerHTML]=\"secondary(item)?.value\"\n ></span>\n } @else if (secondary(item)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"secondary(item)\"\n ></praxis-list-metric>\n } @else if (secondary(item)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"secondary(item)\"\n ></praxis-list-compose>\n } @else if (\n secondary(item)?.type === \"component\"\n ) {\n <praxis-list-runtime-component\n [component]=\"secondary(item)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ secondary(item)?.value }}\n }\n </div>\n }\n @if (meta(item); as m) {\n @if (\n (config.templating?.metaPlacement === \"line\" &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")) ||\n (layoutLines > 2 &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\"))\n ) {\n <div\n class=\"tertiary\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (\n config.templating?.metaPrefixIcon;\n as mpi\n ) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n {{ m.value }}\n </div>\n }\n }\n @if (featuresVisible()) {\n <div class=\"features\">\n @for (\n f of config.templating?.features || [];\n track $index;\n let fi = $index\n ) {\n <span\n class=\"feature\"\n [ngClass]=\"\n featureSemanticClass(item, f.expr, f.class)\n \"\n >\n <ng-container\n *ngTemplateOutlet=\"\n featureContent;\n context: { $implicit: item, feature: f }\n \"\n ></ng-container>\n </span>\n }\n </div>\n }\n </div>\n </button>\n <div class=\"list-item-trailing\">\n @if (meta(item); as m) {\n @if (\n !(\n (config.templating?.metaPlacement === \"line\" ||\n layoutLines > 2) &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")\n )\n ) {\n <div\n class=\"meta\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (\n simpleRichContentNodes(m, { slot: \"meta\" });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(m.color)\n ? m.color\n : undefined\n \"\n [ngClass]=\"\n (m.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(m.color, m.variant)\n \"\n >{{ m.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(m);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, m.value)\"\n [color]=\"ratingThemeColor(m)\"\n [style.cssText]=\"ratingIconStyle(m)\"\n ></mat-icon>\n }\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"m.value\"\n [color]=\"\n isThemeColor(m.color)\n ? m.color\n : undefined\n \"\n [style.cssText]=\"iconStyle(m.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img\n [src]=\"m.value\"\n [alt]=\"m.imageAlt || ''\"\n /></span>\n }\n @case (\"html\") {\n <span [innerHTML]=\"m.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"m\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"m\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"m\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>\n @if (\n config.templating?.metaPrefixIcon;\n as mpi2\n ) {\n <mat-icon\n [praxisIcon]=\"mpi2\"\n ></mat-icon>\n }\n {{ m.value }}</span\n >\n }\n }\n }\n </div>\n }\n }\n @if (trailing(item); as tr) {\n <div\n class=\"trailing\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n @if (\n simpleRichContentNodes(tr, {\n imageAlt:\n config.templating?.trailing?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (tr.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr.color)\n ? tr.color\n : undefined\n \"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(tr.color, tr.variant)\n \"\n >{{ tr.value }}</mat-chip\n >\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"\n isThemeColor(tr.color)\n ? tr.color\n : undefined\n \"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n <img\n [src]=\"tr.value\"\n [alt]=\"\n config.templating?.trailing?.imageAlt ||\n ''\n \"\n />\n @if (tr.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(tr.badge?.color)\n ? tr.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (tr.badge?.variant || 'filled') ===\n 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n tr.badge?.color,\n tr.badge?.variant\n )\n \"\n >{{ tr.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(tr);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, tr.value)\"\n [color]=\"ratingThemeColor(tr)\"\n [style.cssText]=\"ratingIconStyle(tr)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n @if (\n item.alertsData !== undefined &&\n item.ownerName\n ) {\n <!-- Component-based rendering for executive trailing -->\n <span class=\"exec-trailing-shell\">\n <praxis-executive-alerts\n [alerts]=\"item.alertsData\"\n ></praxis-executive-alerts>\n <praxis-executive-owner\n [name]=\"item.ownerName\"\n ></praxis-executive-owner>\n </span>\n } @else {\n <span [innerHTML]=\"tr.value\"></span>\n }\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"tr\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"tr\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"tr\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>{{ tr.value }}</span>\n }\n }\n }\n </div>\n }\n @if (showExpandIcon(\"trailing\")) {\n <button\n type=\"button\"\n [praxisIconButton]=\"\n isExpanded(item, i)\n ? 'expand_less'\n : 'expand_more'\n \"\n size=\"compact\"\n class=\"expand-toggle\"\n [attr.aria-label]=\"expandToggleAriaLabel(item, i)\"\n [attr.aria-expanded]=\"\n expansionOwnedByIcon()\n ? isExpanded(item, i)\n : null\n \"\n [attr.aria-controls]=\"\n expansionOwnedByIcon()\n ? expandRegionId(item, i)\n : null\n \"\n (click)=\"onExpandToggle($event, item, i)\"\n ></button>\n }\n @if ((visibleActions(item, \"trailing\") || []).length) {\n <div\n class=\"list-item-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(item, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"\n action.icon || 'more_horiz'\n \"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, item, i)\"\n [style.cssText]=\"\n actionIconStyle(action.color)\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'stroked')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"\n action.label || action.id\n \"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'raised')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"\n action.label || action.id\n \"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant ||\n action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color)\n ? action.color\n : undefined\n \"\n [style.cssText]=\"\n buttonStyle(action.color, 'flat')\n \"\n (click)=\"\n onActionClick($event, action.id, item, i)\n \"\n [attr.aria-label]=\"\n action.label || action.id\n \"\n [disabled]=\"\n isActionLoading(action.id, item, i)\n \"\n [ngClass]=\"{\n 'action-loading': isActionLoading(\n action.id,\n item,\n i\n ),\n }\"\n >\n @if (isActionLoading(action.id, item, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n </div>\n </div>\n }\n @if (isSelectionEnabled()) {\n <div class=\"list-selection-control\">\n @if (config.selection?.mode === \"multiple\") {\n <mat-checkbox\n [checked]=\"isItemSelected(item)\"\n [disabled]=\"boundControl.disabled\"\n [aria-label]=\"selectionControlAriaLabel(item)\"\n (change)=\"setItemSelected(item, $event.checked)\"\n ></mat-checkbox>\n } @else {\n <mat-radio-button\n [name]=\"selectionControlName()\"\n [checked]=\"isItemSelected(item)\"\n [disabled]=\"boundControl.disabled\"\n [aria-label]=\"selectionControlAriaLabel(item)\"\n (change)=\"setItemSelected(item, true)\"\n ></mat-radio-button>\n }\n </div>\n }\n </div>\n @if (isExpandable() && isExpanded(item, i)) {\n <div\n class=\"item-expansion\"\n [ngClass]=\"expansionRenderingClasses()\"\n [style]=\"expansionRenderingStyle()\"\n [id]=\"expandRegionId(item, i)\"\n role=\"region\"\n [attr.aria-label]=\"expansionRegionAriaLabel(item)\"\n >\n <div class=\"item-expansion-grid\">\n @for (\n expansionSection of expansionSections(item);\n track expansionSection.id\n ) {\n <section\n class=\"expansion-section\"\n [attr.data-section-type]=\"expansionSection.type\"\n >\n @if (expansionSection.title) {\n <div class=\"expansion-section-title\">\n {{ expansionSection.title }}\n </div>\n }\n @if (\n expansionSectionHasContent(\n item,\n expansionSection,\n i\n )\n ) {\n @switch (expansionSection.type) {\n @case (\"chip-list\") {\n <div class=\"expansion-chip-list\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <mat-chip class=\"expansion-chip\">{{\n expansionItemLabel(expansionEntry)\n }}</mat-chip>\n }\n </div>\n }\n @case (\"timeline\") {\n <div class=\"expansion-timeline\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-timeline-item\">\n <div class=\"expansion-timeline-title\">\n {{\n expansionTimelineTitle(expansionEntry)\n }}\n </div>\n @if (\n expansionTimelineMeta(expansionEntry)\n ) {\n <div class=\"expansion-timeline-meta\">\n {{\n expansionTimelineMeta(\n expansionEntry\n )\n }}\n </div>\n }\n @if (\n expansionTimelineDescription(\n expansionEntry\n )\n ) {\n <div\n class=\"expansion-timeline-description\"\n >\n {{\n expansionTimelineDescription(\n expansionEntry\n )\n }}\n </div>\n }\n </div>\n }\n </div>\n }\n @case (\"key-value\") {\n <dl class=\"expansion-key-value\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-key-value-row\">\n @if (\n expansionKeyValueKey(expansionEntry)\n ) {\n <dt>\n {{\n expansionKeyValueKey(expansionEntry)\n }}\n </dt>\n }\n <dd>\n {{\n expansionKeyValueValue(expansionEntry)\n }}\n </dd>\n </div>\n }\n </dl>\n }\n @case (\"rich-content\") {\n @if (\n expansionRichContentDocument(\n item,\n expansionSection,\n i\n );\n as richExpansionDocument\n ) {\n <praxis-rich-content\n [document]=\"richExpansionDocument\"\n [context]=\"\n expansionRichContentContext(\n item,\n expansionSection,\n i\n )\n \"\n [hostCapabilities]=\"\n expansionRichContentHostCapabilities(\n item,\n expansionSection,\n i\n )\n \"\n rootClassName=\"praxis-list-expansion-rich-content\"\n ></praxis-rich-content>\n }\n }\n @default {\n <div class=\"expansion-info-list\">\n @for (\n expansionEntry of expansionSectionItems(\n item,\n expansionSection,\n i\n );\n track $index\n ) {\n <div class=\"expansion-info-item\">\n <div class=\"expansion-info-title\">\n {{ expansionInfoTitle(expansionEntry) }}\n </div>\n @if (expansionInfoValue(expansionEntry)) {\n <div class=\"expansion-info-value\">\n {{\n expansionInfoValue(expansionEntry)\n }}\n </div>\n }\n </div>\n }\n </div>\n }\n }\n } @else {\n <div class=\"expansion-empty\">\n {{\n expansionSection.emptyLabel ||\n \"Sem dados dispon\u00EDveis\"\n }}\n </div>\n }\n </section>\n }\n </div>\n </div>\n }\n </mat-list-item>\n @if (\n (config.layout?.dividers === \"all\" ||\n config.layout?.dividers === \"between\") &&\n i < section.items.length - 1\n ) {\n <mat-divider\n role=\"presentation\"\n aria-hidden=\"true\"\n ></mat-divider>\n }\n }\n }\n </mat-list>\n </ng-template>\n } @else if (isTilesVariant()) {\n <ng-container *ngTemplateOutlet=\"tilesVariant\"></ng-container>\n } @else {\n <ng-container *ngTemplateOutlet=\"cardsVariant\"></ng-container>\n }\n\n <!-- Cards variant -->\n <ng-template #cardsVariant>\n <div class=\"cards-grid\">\n @for (it of (items$ | async) ?? []; track $index; let i = $index) {\n <div\n class=\"item-card\"\n [ngClass]=\"itemRuleClass(it)\"\n [attr.style]=\"itemRuleStyle(it)\"\n >\n <div\n class=\"list-item-content card-main-action\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"itemAriaLabel(it)\"\n (click)=\"onItemClick(it, i)\"\n (keydown.enter)=\"onItemClick(it, i)\"\n (keydown.space)=\"$event.preventDefault(); onItemClick(it, i)\"\n >\n <div class=\"list-item-leading\">\n @if (leading(it); as lead) {\n @if (\n simpleRichContentNodes(lead, {\n imageAlt: config.templating?.leading?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"lead.value\"\n [ngClass]=\"lead.class\"\n [color]=\"\n isThemeColor(lead.color) ? lead.color : undefined\n \"\n [style.cssText]=\"\n (lead.style ? lead.style + ';' : '') +\n iconStyle(lead.color)\n \"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >\n <img\n [src]=\"lead.value\"\n [alt]=\"config.templating?.leading?.imageAlt || ''\"\n />\n @if (lead.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(lead.badge?.color)\n ? lead.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (lead.badge?.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n lead.badge?.color,\n lead.badge?.variant\n )\n \"\n >{{ lead.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"text\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(lead.color) ? lead.color : undefined\n \"\n [ngClass]=\"\n (lead.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(lead.color, lead.variant)\"\n >{{ lead.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(lead);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, lead.value)\"\n [color]=\"ratingThemeColor(lead)\"\n [style.cssText]=\"ratingIconStyle(lead)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n [innerHTML]=\"lead.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"lead\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"lead\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"lead\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n }\n }\n } @else {\n <span class=\"leading-placeholder\"></span>\n }\n </div>\n <div class=\"list-item-text\">\n <div\n class=\"primary\"\n [ngClass]=\"primary(it)?.class\"\n [style.cssText]=\"primary(it)?.style\"\n >\n @if (simpleRichContentNodes(primary(it)); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (primary(it)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"primary(it)\"\n ></praxis-list-metric>\n } @else if (primary(it)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"primary(it)\"\n ></praxis-list-compose>\n } @else if (primary(it)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"primary(it)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ primary(it)?.value }}\n }\n </div>\n @if (layoutLines > 1) {\n <div\n class=\"secondary\"\n [ngClass]=\"secondary(it)?.class\"\n [style.cssText]=\"secondary(it)?.style\"\n >\n @if (simpleRichContentNodes(secondary(it)); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (secondary(it)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"secondary(it)\"\n ></praxis-list-metric>\n } @else if (secondary(it)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"secondary(it)\"\n ></praxis-list-compose>\n } @else if (secondary(it)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"secondary(it)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ secondary(it)?.value }}\n }\n </div>\n }\n @if (meta(it); as m) {\n @if (\n (config.templating?.metaPlacement === \"line\" &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")) ||\n (layoutLines > 2 &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\"))\n ) {\n <div\n class=\"tertiary\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (config.templating?.metaPrefixIcon; as mpi) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n {{ m.value }}\n </div>\n }\n }\n @if (featuresVisible()) {\n <div class=\"features\">\n @for (\n f of config.templating?.features || [];\n track $index;\n let fi = $index\n ) {\n <span\n class=\"feature\"\n [ngClass]=\"featureSemanticClass(it, f.expr, f.class)\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n featureContent;\n context: { $implicit: it, feature: f }\n \"\n ></ng-container>\n </span>\n }\n </div>\n }\n @if (cardDetailSlots(it); as detailSlots) {\n @if (detailSlots.length) {\n <div class=\"card-detail-strip\">\n @for (detail of detailSlots; track detail.slot) {\n <div\n class=\"card-detail\"\n [ngClass]=\"'card-detail--' + detail.slot\"\n >\n <span class=\"card-detail__label\">{{\n detail.label\n }}</span>\n <span class=\"card-detail__value\">\n <ng-container\n *ngTemplateOutlet=\"\n rowLayoutNode;\n context: {\n $implicit: detail.node,\n slot: detail.slot,\n imageAlt: rowLayoutImageAlt(detail.slot),\n }\n \"\n ></ng-container>\n </span>\n </div>\n }\n </div>\n }\n }\n </div>\n <div class=\"list-item-trailing\">\n @if (meta(it); as m) {\n @if (\n !(\n (config.templating?.metaPlacement === \"line\" ||\n layoutLines > 2) &&\n (m?.type === \"text\" ||\n m?.type === \"date\" ||\n m?.type === \"currency\")\n )\n ) {\n <div\n class=\"meta\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (\n simpleRichContentNodes(m, {\n slot: \"meta\",\n imageAlt: m.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(m.color) ? m.color : undefined\n \"\n [ngClass]=\"\n (m.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(m.color, m.variant)\"\n >{{ m.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(m);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, m.value)\"\n [color]=\"ratingThemeColor(m)\"\n [style.cssText]=\"ratingIconStyle(m)\"\n ></mat-icon>\n }\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"m.value\"\n [color]=\"\n isThemeColor(m.color) ? m.color : undefined\n \"\n [style.cssText]=\"iconStyle(m.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img [src]=\"m.value\" [alt]=\"m.imageAlt || ''\"\n /></span>\n }\n @case (\"html\") {\n <span [innerHTML]=\"m.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"m\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"m\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"m\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>\n @if (config.templating?.metaPrefixIcon; as mpi2) {\n <mat-icon [praxisIcon]=\"mpi2\"></mat-icon>\n }\n {{ m.value }}</span\n >\n }\n }\n }\n </div>\n }\n }\n @if (trailing(it); as tr) {\n @if (\n (config.templating?.statusPosition || \"inline\") ===\n \"top-right\" &&\n (tr?.type === \"chip\" || tr?.type === \"icon\")\n ) {\n <div class=\"status-overlay\">\n @if (simpleRichContentNodes(tr); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @if (tr?.type === \"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(tr.color, tr.variant)\"\n >{{ tr.value }}</mat-chip\n >\n }\n @if (tr?.type === \"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n }\n </div>\n } @else {\n <div\n class=\"trailing\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n @if (\n simpleRichContentNodes(tr, {\n imageAlt: config.templating?.trailing?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (tr.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(tr.color, tr.variant)\"\n >{{ tr.value }}</mat-chip\n >\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <div\n class=\"lead-image\"\n [ngClass]=\"tr.class\"\n [style.cssText]=\"tr.style\"\n >\n <img\n [src]=\"tr.value\"\n [alt]=\"\n config.templating?.trailing?.imageAlt || ''\n \"\n />\n @if (tr.badge?.value) {\n <mat-chip\n class=\"lead-badge\"\n [color]=\"\n isThemeColor(tr.badge?.color)\n ? tr.badge?.color\n : undefined\n \"\n [ngClass]=\"\n (tr.badge?.variant || 'filled') ===\n 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(\n tr.badge?.color,\n tr.badge?.variant\n )\n \"\n >{{ tr.badge?.value }}</mat-chip\n >\n }\n </div>\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(tr);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, tr.value)\"\n [color]=\"ratingThemeColor(tr)\"\n [style.cssText]=\"ratingIconStyle(tr)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n <span [innerHTML]=\"tr.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"tr\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"tr\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"tr\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>{{ tr.value }}</span>\n }\n }\n }\n </div>\n }\n }\n </div>\n </div>\n <div\n class=\"card-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(it, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, it, i)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'stroked')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'raised')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant || action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'flat')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n </div>\n }\n </div>\n </ng-template>\n\n <!-- Tiles variant -->\n <ng-template #tilesVariant>\n <div class=\"tiles-grid\">\n @for (it of (items$ | async) ?? []; track $index; let i = $index) {\n <div\n class=\"item-tile\"\n [ngClass]=\"itemRuleClass(it)\"\n [attr.style]=\"itemRuleStyle(it)\"\n >\n <div\n class=\"tile-main-action\"\n role=\"button\"\n tabindex=\"0\"\n [attr.aria-label]=\"itemAriaLabel(it)\"\n (click)=\"onItemClick(it, i)\"\n (keydown.enter)=\"onItemClick(it, i)\"\n (keydown.space)=\"$event.preventDefault(); onItemClick(it, i)\"\n >\n <div class=\"tile-media\">\n @if (leading(it); as lead) {\n @if (\n simpleRichContentNodes(lead, {\n imageAlt: config.templating?.leading?.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (lead.type) {\n @case (\"image\") {\n <img\n [src]=\"lead.value\"\n [alt]=\"config.templating?.leading?.imageAlt || ''\"\n />\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"lead.value\"\n [ngClass]=\"lead.class\"\n [color]=\"\n isThemeColor(lead.color) ? lead.color : undefined\n \"\n [style.cssText]=\"\n (lead.style ? lead.style + ';' : '') +\n iconStyle(lead.color)\n \"\n ></mat-icon>\n }\n @case (\"text\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(lead.color) ? lead.color : undefined\n \"\n [ngClass]=\"\n (lead.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(lead.color, lead.variant)\"\n >{{ lead.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >\n @for (\n _ of ratingRange(lead);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, lead.value)\"\n [color]=\"ratingThemeColor(lead)\"\n [style.cssText]=\"ratingIconStyle(lead)\"\n ></mat-icon>\n }\n </span>\n }\n @case (\"html\") {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n [innerHTML]=\"lead.value\"\n ></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"lead\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"lead\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"lead\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span\n [ngClass]=\"lead.class\"\n [style.cssText]=\"lead.style\"\n >{{ lead.value }}</span\n >\n }\n }\n }\n }\n </div>\n\n @if (trailing(it); as tr) {\n @if (\n (config.templating?.statusPosition || \"inline\") ===\n \"top-right\" &&\n (tr?.type === \"chip\" || tr?.type === \"icon\")\n ) {\n <div class=\"tile-status\">\n @if (simpleRichContentNodes(tr); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @if (tr?.type === \"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [ngClass]=\"\n (tr.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(tr.color, tr.variant)\"\n >{{ tr.value }}</mat-chip\n >\n }\n @if (tr?.type === \"icon\") {\n <mat-icon\n [praxisIcon]=\"tr.value\"\n [color]=\"\n isThemeColor(tr.color) ? tr.color : undefined\n \"\n [style.cssText]=\"iconStyle(tr.color)\"\n ></mat-icon>\n }\n }\n </div>\n }\n }\n\n <div class=\"tile-body\">\n <div\n class=\"primary\"\n [ngClass]=\"primary(it)?.class\"\n [style.cssText]=\"primary(it)?.style\"\n >\n @if (simpleRichContentNodes(primary(it)); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (primary(it)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"primary(it)\"\n ></praxis-list-metric>\n } @else if (primary(it)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"primary(it)\"\n ></praxis-list-compose>\n } @else if (primary(it)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"primary(it)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ primary(it)?.value }}\n }\n </div>\n @if (layoutLines > 1) {\n <div\n class=\"secondary\"\n [ngClass]=\"secondary(it)?.class\"\n [style.cssText]=\"secondary(it)?.style\"\n >\n @if (simpleRichContentNodes(secondary(it)); as richNodes) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else if (secondary(it)?.type === \"metric\") {\n <praxis-list-metric\n [metric]=\"secondary(it)\"\n ></praxis-list-metric>\n } @else if (secondary(it)?.type === \"compose\") {\n <praxis-list-compose\n [compose]=\"secondary(it)\"\n ></praxis-list-compose>\n } @else if (secondary(it)?.type === \"component\") {\n <praxis-list-runtime-component\n [component]=\"secondary(it)\"\n ></praxis-list-runtime-component>\n } @else {\n {{ secondary(it)?.value }}\n }\n </div>\n }\n\n @if (meta(it); as m) {\n <div\n class=\"tile-meta\"\n [ngClass]=\"m.class\"\n [style.cssText]=\"m.style\"\n >\n @if (config.templating?.metaPrefixIcon; as mpi) {\n <mat-icon [praxisIcon]=\"mpi\"></mat-icon>\n }\n @if (\n simpleRichContentNodes(m, {\n slot: \"meta\",\n imageAlt: m.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (m.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(m.color) ? m.color : undefined\n \"\n [ngClass]=\"\n (m.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"chipStyle(m.color, m.variant)\"\n >{{ m.value }}</mat-chip\n >\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(m);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, m.value)\"\n [color]=\"ratingThemeColor(m)\"\n [style.cssText]=\"ratingIconStyle(m)\"\n ></mat-icon>\n }\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"m.value\"\n [color]=\"\n isThemeColor(m.color) ? m.color : undefined\n \"\n [style.cssText]=\"iconStyle(m.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img [src]=\"m.value\" [alt]=\"m.imageAlt || ''\"\n /></span>\n }\n @case (\"html\") {\n <span [innerHTML]=\"m.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric [metric]=\"m\"></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"m\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"m\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>{{ m.value }}</span>\n }\n }\n }\n </div>\n }\n\n @if (featuresVisible()) {\n <div class=\"tiles-features\">\n @for (\n f of config.templating?.features || [];\n track $index;\n let fi = $index\n ) {\n <span\n class=\"feature\"\n [ngClass]=\"featureSemanticClass(it, f.expr, f.class)\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n featureContent;\n context: { $implicit: it, feature: f }\n \"\n ></ng-container>\n </span>\n }\n </div>\n }\n\n @if (trailing(it); as tr2) {\n @if (\n !(\n (config.templating?.statusPosition || \"inline\") ===\n \"top-right\" &&\n (tr2?.type === \"chip\" || tr2?.type === \"icon\")\n )\n ) {\n <div\n class=\"tile-trailing\"\n [ngClass]=\"tr2.class\"\n [style.cssText]=\"tr2.style\"\n >\n @if (\n simpleRichContentNodes(tr2, {\n imageAlt: tr2.imageAlt || \"\",\n });\n as richNodes\n ) {\n <praxis-rich-content\n [nodes]=\"richNodes\"\n ></praxis-rich-content>\n } @else {\n @switch (tr2.type) {\n @case (\"chip\") {\n <mat-chip\n [color]=\"\n isThemeColor(tr2.color) ? tr2.color : undefined\n \"\n [ngClass]=\"\n (tr2.variant || 'filled') === 'outlined'\n ? 'chip-outlined'\n : ''\n \"\n [style.cssText]=\"\n chipStyle(tr2.color, tr2.variant)\n \"\n >{{ tr2.value }}</mat-chip\n >\n }\n @case (\"icon\") {\n <mat-icon\n [praxisIcon]=\"tr2.value\"\n [color]=\"\n isThemeColor(tr2.color) ? tr2.color : undefined\n \"\n [style.cssText]=\"iconStyle(tr2.color)\"\n ></mat-icon>\n }\n @case (\"image\") {\n <span class=\"inline-image\"\n ><img\n [src]=\"tr2.value\"\n [alt]=\"tr2.imageAlt || ''\"\n /></span>\n }\n @case (\"rating\") {\n @for (\n _ of ratingRange(tr2);\n track $index;\n let idx = $index\n ) {\n <mat-icon\n [praxisIcon]=\"starIcon(idx, tr2.value)\"\n [color]=\"ratingThemeColor(tr2)\"\n [style.cssText]=\"ratingIconStyle(tr2)\"\n ></mat-icon>\n }\n }\n @case (\"html\") {\n <span [innerHTML]=\"tr2.value\"></span>\n }\n @case (\"metric\") {\n <praxis-list-metric\n [metric]=\"tr2\"\n ></praxis-list-metric>\n }\n @case (\"compose\") {\n <praxis-list-compose\n [compose]=\"tr2\"\n ></praxis-list-compose>\n }\n @case (\"component\") {\n <praxis-list-runtime-component\n [component]=\"tr2\"\n ></praxis-list-runtime-component>\n }\n @default {\n <span>{{ tr2.value }}</span>\n }\n }\n }\n </div>\n }\n }\n </div>\n </div>\n\n @if ((visibleActions(it, \"trailing\") || []).length) {\n <div\n class=\"tile-actions\"\n (click)=\"$event.stopPropagation()\"\n (keydown.enter)=\"onActionKeydown($event)\"\n (keydown.space)=\"onActionKeydown($event)\"\n >\n @for (\n action of visibleActions(it, \"trailing\");\n let aidx = $index;\n track action?.id ?? $index\n ) {\n @if ((action.kind || \"icon\") === \"icon\") {\n <button\n type=\"button\"\n [praxisIconButton]=\"action.icon || 'more_horiz'\"\n size=\"compact\"\n [busy]=\"isActionLoading(action.id, it, i)\"\n [style.cssText]=\"actionIconStyle(action.color)\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n ></button>\n } @else {\n @if (action.buttonVariant === \"stroked\") {\n <button\n mat-stroked-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'stroked')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (action.buttonVariant === \"raised\") {\n <button\n mat-raised-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'raised')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n @if (\n !action.buttonVariant || action.buttonVariant === \"flat\"\n ) {\n <button\n mat-flat-button\n [color]=\"\n isThemeColor(action.color) ? action.color : undefined\n \"\n [style.cssText]=\"buttonStyle(action.color, 'flat')\"\n (click)=\"onActionClick($event, action.id, it, i)\"\n [attr.aria-label]=\"action.label || action.id\"\n [disabled]=\"isActionLoading(action.id, it, i)\"\n [ngClass]=\"{\n 'action-loading': isActionLoading(action.id, it, i),\n }\"\n >\n @if (isActionLoading(action.id, it, i)) {\n <mat-progress-spinner\n class=\"action-spinner\"\n mode=\"indeterminate\"\n diameter=\"16\"\n strokeWidth=\"3\"\n ></mat-progress-spinner>\n } @else {\n {{ action.label || action.id }}\n }\n </button>\n }\n }\n }\n </div>\n }\n </div>\n }\n </div>\n </ng-template>\n\n <!-- Remote pagination stays attached to the collection footer. -->\n @if (isRemoteDataMode() && !(error$ | async)) {\n @if (page$ | async; as ps) {\n @if (total$ | async; as total) {\n @if (config.ui?.showRange === false) {\n <div class=\"list-paginator-total\" aria-live=\"polite\">\n {{ remoteTotalLabel(total) }}\n </div>\n }\n <mat-paginator\n [length]=\"total || 0\"\n [pageIndex]=\"ps.pageNumber || 0\"\n [pageSize]=\"ps.pageSize || config.layout?.pageSize || 10\"\n [pageSizeOptions]=\"listPageSizeOptions()\"\n [selectConfig]=\"paginatorSelectConfig\"\n [showFirstLastButtons]=\"true\"\n [class.hide-range]=\"config.ui?.showRange === false\"\n (page)=\"onPageChange($event)\"\n >\n </mat-paginator>\n }\n }\n }\n </ng-template>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block}.praxis-list-root{container-type:inline-size;--p-list-radius: var(--md-sys-shape-corner-medium, 16px);--p-list-shadow: var(--md-sys-elevation-level2);--p-list-border: 1px solid var(--md-sys-color-outline-variant);--p-list-blur: 10px;--p-list-surface: var(--md-sys-color-surface-container);--p-list-surface-low: var(--md-sys-color-surface);--p-list-surface-high: var( --md-sys-color-surface-container-high, var(--md-sys-color-surface-container) );--p-list-foreground: var(--md-sys-color-on-surface);--p-list-foreground-muted: var(--md-sys-color-on-surface-variant);--p-list-accent: var(--md-sys-color-primary);--p-list-accent-weak: var(--md-sys-color-primary-container);--p-list-item-surface: var( --md-sys-color-surface-container-low, var(--md-sys-color-surface) );--p-list-item-border: 1px solid var(--md-sys-color-outline-variant);--p-list-item-hover-surface: var(--md-sys-color-surface-container-low);--p-list-item-active-surface: var(--md-sys-color-surface-container);--p-list-item-selected-surface: var(--md-sys-color-surface-container);--p-list-grad-from: var(--md-sys-color-primary-container);--p-list-grad-to: var(--md-sys-color-tertiary-container);--p-list-grad-angle: 135deg;--p-list-grad-foreground: var( --md-sys-color-on-primary-container, var(--md-sys-color-on-surface) );--p-list-grad-foreground-muted: color-mix( in srgb, var(--p-list-grad-foreground) 76%, transparent );--p-list-leading-width: 36px;--p-list-trailing-min-width: 140px;--p-list-trailing-max-width: 320px;--p-list-item-gap: 10px;--p-list-item-padding-x: 14px;--p-list-item-padding-y: 10px;--p-list-item-stack-gap: 0px;--p-list-meta-size: .875rem;--p-list-meta-weight: 500;--p-list-chip-height: 22px;--p-list-chip-font-size: 12px;--p-list-trailing-padding-right: 8px;--p-list-action-button-height: 32px;--p-list-tile-minW: 240px;--p-list-tile-gap: 12px;--p-list-tile-padding: 12px;--p-list-tile-radius: 12px;--p-list-tile-media-radius: 12px;--p-list-tile-media-ratio: 1 / 1;--p-list-tile-hover-overlay: .04;--p-list-tile-press-overlay: .08;--p-list-tile-press-scale: .99}.list-assistant{display:flex;justify-content:flex-end;gap:8px;padding:4px 4px 8px}.action-loading{opacity:.65}.action-spinner{width:16px;height:16px;display:inline-flex;align-items:center;justify-content:center}.action-loading .mat-mdc-button-touch-target,.action-loading .mdc-button__label{opacity:.7}.skin-elevated,.skin-outline,.skin-flat,.skin-neumorphism{--p-list-item-surface: var( --mdc-elevated-card-container-color, var(--p-list-surface) );--p-list-item-border: var(--p-list-border)}.skin-elevated .item-card,.skin-outline .item-card,.skin-flat .item-card,.skin-neumorphism .item-card{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:var(--p-list-radius);box-shadow:var(--p-list-shadow);border:var(--p-list-border)}.skin-elevated .item-tile,.skin-outline .item-tile,.skin-flat .item-tile,.skin-neumorphism .item-tile{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));box-shadow:var(--p-list-shadow);border:var(--p-list-border)}.skin-elevated .mat-mdc-list-item .list-item-content,.skin-outline .mat-mdc-list-item .list-item-content,.skin-flat .mat-mdc-list-item .list-item-content,.skin-neumorphism .mat-mdc-list-item .list-item-content{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * .75);box-shadow:var(--p-list-shadow);border:var(--p-list-item-border);color:var(--p-list-foreground)}.skin-outline{--p-list-shadow: none;--p-list-border: 1px solid var(--md-sys-color-outline)}.skin-flat{--p-list-shadow: none;--p-list-border: 1px solid var(--md-sys-color-outline-variant)}.skin-neumorphism{--p-list-shadow: var(--md-sys-elevation-level2);--p-list-border: 1px solid var(--md-sys-color-outline-variant);--p-list-radius: 1.25rem}.skin-pill-soft .item-card,.skin-pill-soft .item-tile{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:var(--md-sys-elevation-level1);border:var(--p-list-border)}.skin-pill-soft .mat-mdc-list-item .list-item-content{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:var(--md-sys-elevation-level1);border:var(--p-list-border);color:var(--p-list-foreground)}.skin-gradient-tile{--p-list-foreground: var(--p-list-grad-foreground);--p-list-foreground-muted: var(--p-list-grad-foreground-muted);--p-list-item-hover-surface: linear-gradient( var(--p-list-grad-angle), var(--p-list-grad-from), var(--p-list-grad-to) );--p-list-item-active-surface: linear-gradient( var(--p-list-grad-angle), var(--p-list-grad-from), var(--p-list-grad-to) );--p-list-item-selected-surface: linear-gradient( var(--p-list-grad-angle), var(--p-list-grad-from), var(--p-list-grad-to) )}.skin-gradient-tile .item-card,.skin-gradient-tile .item-tile{background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));border-radius:var(--p-list-radius);color:var(--p-list-foreground)}.skin-gradient-tile .tile-media{background:color-mix(in srgb,var(--md-sys-color-surface) 74%,transparent);border-color:color-mix(in srgb,var(--p-list-foreground) 22%,transparent)}.skin-gradient-tile .tile-media mat-icon,.skin-gradient-tile .tile-meta,.skin-gradient-tile .tile-trailing,.skin-gradient-tile .feature,.skin-gradient-tile .secondary,.skin-gradient-tile .meta{color:var(--p-list-foreground-muted)}.skin-gradient-tile .tile-status,.skin-gradient-tile [data-rich-node-type=badge] .prx-rich-badge{color:var(--md-sys-color-on-secondary-container, var(--p-list-foreground));background:var(--md-sys-color-secondary-container, rgba(255, 255, 255, .72))}.skin-gradient-tile .mat-mdc-list-item .list-item-content{background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));border-radius:var(--p-list-radius);color:var(--p-list-foreground)}.skin-glass .item-card,.skin-glass .item-tile{background:var(--md-sys-color-surface-container-low);border-radius:var(--p-list-radius);border:1px solid var(--md-sys-color-outline-variant);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur))}.skin-glass .mat-mdc-list-item .list-item-content{background:var(--md-sys-color-surface-container-low);border-radius:var(--p-list-radius);border:1px solid var(--md-sys-color-outline-variant);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur));color:var(--p-list-foreground)}.density-compact .mat-mdc-list-item,.density-compact .item-card{--p-list-item-padding-y: 6px;--p-list-item-padding-x: 10px;--p-list-item-gap: 8px}.density-comfortable .mat-mdc-list-item,.density-comfortable .item-card{--p-list-item-padding-y: 8px;--p-list-item-padding-x: 12px;--p-list-item-gap: 10px}.praxis-list-root.lines-3 .mat-mdc-list-item{min-height:68px}.density-compact{--p-list-tile-gap: 10px;--p-list-tile-padding: 10px;--p-list-tile-minW: 188px}.density-comfortable{--p-list-tile-gap: 12px;--p-list-tile-padding: 12px}.item-spacing-none{--p-list-item-stack-gap: 0px}.item-spacing-tight{--p-list-item-stack-gap: 4px;--p-list-tile-gap: 10px}.item-spacing-default{--p-list-item-stack-gap: 6px;--p-list-tile-gap: 12px}.item-spacing-relaxed{--p-list-item-stack-gap: 14px;--p-list-tile-gap: 20px}.list-item-content{box-sizing:border-box;display:grid;grid-template-columns:var(--p-list-leading-width) minmax(0,1fr) minmax(var(--p-list-trailing-min-width),min(34vw,var(--p-list-trailing-max-width)));align-items:center;gap:var(--p-list-item-gap);padding:var(--p-list-item-padding-y) var(--p-list-item-padding-x);width:100%;min-width:0;border-radius:calc(var(--p-list-radius) * .75);overflow:visible}.list-item-content--row-layout{--p-list-row-template-columns: var(--p-list-leading-width) minmax(0, 1fr) minmax(var(--p-list-trailing-min-width), max-content);--p-list-row-gap: var(--p-list-item-gap);--p-list-row-align-items: center;grid-template-columns:var(--p-list-row-template-columns);gap:var(--p-list-row-gap);align-items:var(--p-list-row-align-items);position:relative}.list-row-main-action{position:absolute;inset:0;z-index:1;padding:0;border:0;border-radius:inherit;background:transparent;cursor:pointer}.list-row-main-action:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.list-row-slot{position:relative;z-index:0;min-width:0;height:100%;align-self:stretch;display:flex;align-items:var(--p-list-row-slot-align-items, center);gap:8px}.list-item-content--row-layout :is(.list-row-slot--actions,.list-row-slot--expand,.list-row-slot--trailing){z-index:2}.list-item-content--row-layout .list-row-slot>praxis-rich-content,.list-item-content--row-layout .list-row-slot>praxis-list-metric,.list-item-content--row-layout .list-row-slot>praxis-list-compose,.list-item-content--row-layout .list-row-slot>praxis-list-runtime-component{min-width:0}.list-row-slot--leading{justify-content:center}.list-row-slot--primary,.list-row-slot--secondary{min-width:0}.list-row-slot--meta{color:var(--p-list-foreground);font-variant-numeric:tabular-nums}.list-row-slot--trailing{flex-wrap:wrap;justify-content:flex-end}.list-row-slot .primary,.list-row-slot .secondary,.list-row-slot .meta,.list-row-slot .trailing{min-width:0}.list-item-main-action{grid-column:1/3;display:grid;grid-template-columns:var(--p-list-leading-width) minmax(0,1fr);align-items:center;gap:var(--p-list-item-gap);min-width:0;padding:0;border:0;background:transparent;color:inherit;text-align:start;font:inherit;cursor:pointer}.list-item-main-action:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px;border-radius:12px}.list-item-leading{display:flex;align-items:center;justify-content:center;min-height:100%}.leading-placeholder{width:24px;height:24px;display:inline-block}.list-item-text{min-width:0;display:grid;align-content:center;gap:2px}.list-item-trailing{box-sizing:border-box;min-width:0;width:100%;max-width:var(--p-list-trailing-max-width);display:grid;align-self:center;align-items:center;justify-content:end;justify-items:end;align-content:center;gap:4px;text-align:end;padding-right:var(--p-list-trailing-padding-right)}.list-item-trailing>*{align-self:center}.list-item-content--row-layout .list-item-actions{justify-content:flex-end}.list-item-actions{width:max-content;justify-self:end;align-self:center;display:flex;align-items:center;justify-content:flex-end;gap:4px;flex-wrap:wrap;max-width:100%;min-height:var(--p-list-action-button-height)}.list-item-actions .mat-mdc-button-base{min-height:var(--p-list-action-button-height);height:var(--p-list-action-button-height);display:inline-flex;align-items:center}.expand-toggle{color:var(--p-list-foreground-muted)}.mat-mdc-list-item .list-item-content{background:var(--p-list-item-surface);border:var(--p-list-item-border);transition:background .15s ease,box-shadow .15s ease,transform .15s ease}.mat-mdc-list-item{height:auto;align-items:stretch;overflow:visible}.mat-mdc-list-item.mdc-list-item--with-one-line,.mat-mdc-list-item.mdc-list-item--with-two-lines,.mat-mdc-list-item.mdc-list-item--with-three-lines,.mat-mdc-list-item-single-line,.mat-mdc-list-item-two-line,.mat-mdc-list-item-three-line{height:auto!important}.mdc-list-item{overflow:visible}.density-compact .mat-mdc-list-item{min-height:40px}.density-comfortable .mat-mdc-list-item{min-height:46px}.praxis-list-root:not(.density-compact):not(.density-comfortable) .mat-mdc-list-item{min-height:48px}.mat-mdc-list-item .mdc-list-item__content,.mat-mdc-list-item .mdc-list-item__primary-text{display:block!important;width:100%!important;height:auto!important;padding:0!important;margin:0!important;overflow:visible!important;box-sizing:border-box}.mat-mdc-list-item{padding:0!important;box-sizing:border-box}.praxis-list-root mat-list>mat-list-item:not(:last-child){margin-bottom:var(--p-list-item-stack-gap)}.mat-mdc-list-item:hover .list-item-content{background:var(--p-list-item-hover-surface)}.mat-mdc-list-item:active .list-item-content{background:var(--p-list-item-active-surface)}.mat-mdc-list-item:focus-visible .list-item-content{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.list-row-shell{width:100%;min-width:0}.list-row-shell--selectable{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:stretch;border-radius:calc(var(--p-list-radius) * .75);background:var(--p-list-item-surface);border:var(--p-list-item-border);color:var(--p-list-foreground);overflow:hidden;transition:background .15s ease,box-shadow .15s ease,border-color .15s ease}.list-selection-control{position:relative;z-index:2;display:flex;align-items:flex-start;justify-content:center;padding:var(--p-list-item-padding-y) var(--p-list-item-padding-x) var(--p-list-item-padding-y) 0}.praxis-list-root .mat-mdc-list-item .list-row-shell--selectable .list-item-content{background:transparent;border:0;border-radius:0;box-shadow:none}.mat-mdc-list-item:hover .list-row-shell--selectable{background:var(--p-list-item-hover-surface)}.mat-mdc-list-item:active .list-row-shell--selectable{background:var(--p-list-item-active-surface)}.list-row-shell--selected{background:var(--p-list-item-selected-surface);border-color:var(--md-sys-color-outline-variant)}.skin-elevated .list-row-shell--selectable,.skin-outline .list-row-shell--selectable,.skin-flat .list-row-shell--selectable,.skin-neumorphism .list-row-shell--selectable{background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border:var(--p-list-item-border);box-shadow:var(--p-list-shadow)}.skin-pill-soft .list-row-shell--selectable{border-radius:calc(var(--p-list-radius) * 1.3);background:var(--mdc-elevated-card-container-color, var(--p-list-surface));border:var(--p-list-border);box-shadow:var(--md-sys-elevation-level1)}.skin-gradient-tile .list-row-shell--selectable{border-radius:var(--p-list-radius);background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));color:var(--p-list-foreground)}.skin-glass .list-row-shell--selectable{border-radius:var(--p-list-radius);background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur))}.executive-inline-skin{--p-list-leading-width: 56px;--p-list-trailing-min-width: 232px;--p-list-item-padding-x: 14px;--p-list-item-padding-y: 8px;--p-list-item-gap: 10px;--p-list-surface: #f8fafb;--p-list-surface-low: #ffffff;--p-list-item-surface: #ffffff;--p-list-item-hover-surface: #f7fafc;--p-list-item-active-surface: #eef4f8;--p-list-item-selected-surface: #eef4f8;--p-list-foreground: #17324d;--p-list-foreground-muted: #72849a}.executive-inline-skin .list-item-content--row-layout.exec-row-layout{min-height:72px;padding:12px 20px;background:linear-gradient(180deg,#fff,#fbfcfd)!important;border:1px solid #dce5ec!important;border-radius:18px;box-shadow:0 1px 2px #0f223a08,0 4px 14px #0f223a08!important;color:#17324d!important}.executive-inline-skin .mat-mdc-list-item:hover .list-item-content--row-layout.exec-row-layout{background:linear-gradient(180deg,#fff,#f7fafc)!important}.executive-inline-skin .list-item-content--row-layout.exec-row-layout+.item-expansion{margin-left:10px;margin-right:10px}.executive-inline-skin .list-item-content--row-layout.exec-row-layout .list-row-slot{min-height:34px}.executive-inline-skin .list-row-slot--leading{justify-content:center}.executive-inline-skin .list-row-slot--identity{align-items:center;padding-right:4px}.executive-inline-skin .list-row-slot--balance,.executive-inline-skin .list-row-slot--limit,.executive-inline-skin .list-row-slot--risk,.executive-inline-skin .list-row-slot--alerts{justify-content:center;padding-inline:2px}.executive-inline-skin .list-row-slot--owner{justify-content:flex-start;padding-left:0}.executive-inline-skin .list-row-slot--actions{justify-content:flex-end;padding-left:0}.executive-inline-skin .list-row-slot--expand{justify-content:center}.executive-inline-skin .docs-expansion-leading{width:44px;height:44px;display:inline-flex;align-items:center;justify-content:center;border-radius:15px;background:linear-gradient(180deg,#edf4f9,#e6eef5);color:#1b5f86;font-size:1rem;font-weight:800;letter-spacing:.02em;box-shadow:0 1px 3px #1b5f861f;border:1px solid #dbe7f0}.executive-inline-skin .exec-balance-metric,.executive-inline-skin .exec-limit-metric,.executive-inline-skin .exec-risk-metric{min-width:0}.executive-inline-skin .exec-balance-metric .p-list-metric,.executive-inline-skin .exec-limit-metric .p-list-metric,.executive-inline-skin .exec-risk-metric .p-list-metric{gap:5px}.executive-inline-skin .exec-balance-metric .p-list-metric__label,.executive-inline-skin .exec-limit-metric .p-list-metric__label,.executive-inline-skin .exec-risk-metric .p-list-metric__label{font-size:.62rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#8090a1}.executive-inline-skin .exec-balance-metric .p-list-metric__value,.executive-inline-skin .exec-limit-metric .p-list-metric__value{font-size:.96rem;font-weight:800;letter-spacing:-.02em;color:#0d1f34;line-height:1.05}.executive-inline-skin .exec-limit-metric .p-list-metric__caption{font-size:.66rem;font-weight:700;color:#8090a1}.executive-inline-skin .exec-limit-metric .p-list-metric__progress{width:68px;justify-self:center;height:3px}.executive-inline-skin .exec-risk-metric{min-width:84px}.executive-inline-skin .exec-risk-metric .p-list-metric{justify-items:center}.executive-inline-skin .exec-risk-metric .p-list-metric__main{gap:6px}.executive-inline-skin .exec-risk-metric .p-list-metric__value{font-size:1.02rem;font-weight:800;letter-spacing:-.024em;line-height:1}.executive-inline-skin .exec-risk-metric .p-list-metric__progress{width:56px;justify-self:center;height:3px}.executive-inline-skin .exec-risk-metric .p-list-metric__caption{font-size:.7rem;font-weight:700;line-height:1.1}.executive-inline-skin .exec-risk-metric .p-list-metric__caption--below-progress{justify-self:center;margin-top:-1px}.executive-inline-skin .exec-risk-metric .p-list-metric__icon mat-icon{width:15px;height:15px;font-size:15px}.executive-inline-skin .list-item-actions{gap:6px;flex-wrap:nowrap}.executive-inline-skin .list-row-slot--actions .list-item-actions{justify-content:flex-end}.executive-inline-skin .list-item-actions button,.executive-inline-skin .expand-toggle{width:30px;height:30px;min-width:30px;border-radius:12px;color:#6d7f93!important}.executive-inline-skin .list-item-actions button mat-icon,.executive-inline-skin .expand-toggle mat-icon{width:18px;height:18px;font-size:18px}.executive-inline-skin .item-expansion{position:relative;margin-top:-10px;padding:22px 26px;background:linear-gradient(180deg,#fcfdfe,#f5f8fb);border-color:#e4eaf0;border-radius:0 0 22px 22px;box-shadow:inset 0 1px #ffffffdb}.lead-image{position:relative;width:96px;height:72px;border-radius:12px;overflow:hidden}.lead-image img{width:100%;height:100%;object-fit:cover;display:block}.lead-image .lead-badge{position:absolute;left:8px;bottom:8px}.inline-image{position:relative;width:20px;height:20px;border-radius:4px;overflow:hidden;display:inline-block;vertical-align:middle}.inline-image img{width:100%;height:100%;object-fit:cover;display:block}.inline-image .inline-badge{position:absolute;left:2px;bottom:2px}.model-media .lead-image{width:136px;height:96px;border-radius:16px}.model-media .list-item-content{gap:16px}.model-media .item-card{--p-list-leading-width: 136px}.model-hotel .lead-image{width:160px;height:110px;border-radius:18px}.model-hotel .item-card{--p-list-leading-width: 160px}.primary,.secondary{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis}.primary{-webkit-line-clamp:1;font-weight:600;color:var(--p-list-foreground)}.secondary{-webkit-line-clamp:1;color:var(--p-list-foreground-muted)}.lines-3 .tertiary{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.item-tile .primary{-webkit-line-clamp:2;font-size:.95rem;line-height:1.25rem}.item-tile .secondary{-webkit-line-clamp:2;font-size:.8rem;line-height:1.1rem}.tertiary{color:var(--p-list-foreground-muted)}.features{display:flex;flex-wrap:wrap;gap:12px;margin-top:6px;color:inherit}.feature{display:inline-flex;align-items:center;gap:6px}.feature-line{display:block}.feature-progress{display:block;width:88px;height:4px;margin-top:6px;border-radius:999px;background:#dbe5eb;overflow:hidden}.feature-progress__value{display:block;height:100%;border-radius:inherit;background:#2f9b69}.meta{color:var(--p-list-foreground);font-size:var(--p-list-meta-size);font-weight:var(--p-list-meta-weight);font-variant-numeric:tabular-nums;max-width:100%;min-width:0;display:inline-flex;align-items:center;justify-content:flex-end;gap:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.meta>span,.meta praxis-rich-content{min-width:0;display:inline-flex;align-items:center;justify-content:flex-end;gap:4px;line-height:1.15;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.meta .mat-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;line-height:1;vertical-align:middle}.trailing{color:var(--p-list-foreground-muted);margin-left:0;max-width:100%;min-width:0;display:inline-flex;justify-content:flex-end;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.trailing praxis-rich-content{min-width:0}:host ::ng-deep .meta praxis-rich-content .prx-rich-node[data-rich-node-type=badge],:host ::ng-deep .trailing praxis-rich-content .prx-rich-node[data-rich-node-type=badge],:host ::ng-deep .list-row-slot praxis-rich-content .prx-rich-node[data-rich-node-type=badge]{background:transparent;color:inherit}:host ::ng-deep .meta praxis-rich-content .prx-rich-node[data-rich-node-type=badge] .prx-rich-badge,:host ::ng-deep .trailing praxis-rich-content .prx-rich-node[data-rich-node-type=badge] .prx-rich-badge,:host ::ng-deep .list-row-slot praxis-rich-content .prx-rich-node[data-rich-node-type=badge] .prx-rich-badge{min-height:22px;padding:1px 9px;border:1px solid var(--p-list-chip-border-color, transparent);background:var(--p-list-chip-background, var(--md-sys-color-primary-container, #e8def8));color:var(--p-list-chip-color, var(--md-sys-color-on-primary-container, #21005d));line-height:18px}.section-header{font-size:.85rem;color:var(--p-list-foreground-muted);padding:8px 12px}.section-header--setup{margin:8px 12px 12px;padding:14px 16px;border:1px dashed var(--md-sys-color-outline-variant);border-radius:8px;background:color-mix(in srgb,var(--p-list-surface) 92%,var(--md-sys-color-primary) 8%);color:var(--p-list-foreground);font-weight:500}.praxis-list-root mat-list{display:block;padding-bottom:12px}.praxis-list-root .mat-divider{margin-left:var(--p-list-item-padding-x);margin-right:var(--p-list-item-padding-x)}.item-expansion{margin:0 var(--p-list-item-padding-x) var(--p-list-item-stack-gap);padding:var(--p-list-expansion-padding, 14px 16px);background:var(--md-sys-color-surface-container-low, var(--p-list-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:calc(var(--p-list-radius) * .75)}.item-expansion--detached,.item-expansion--modal{margin-top:var(--p-list-item-stack-gap);background:var(--md-sys-color-surface-container, var(--p-list-surface));box-shadow:var(--md-sys-elevation-level1)}.item-expansion--modal{border-color:var(--md-sys-color-outline);box-shadow:var(--md-sys-elevation-level3)}.item-expansion-grid{display:grid;grid-template-columns:var(--p-list-expansion-columns, repeat(auto-fit, minmax(220px, 1fr)));gap:var(--p-list-expansion-gap, 16px)}.expansion-section{min-width:0;display:grid;gap:10px;align-content:start}.expansion-section-title{font-size:.78rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--p-list-foreground-muted)}.expansion-info-list,.expansion-timeline,.expansion-key-value{display:grid;gap:10px}.expansion-info-item,.expansion-timeline-item{display:grid;gap:4px;padding:10px 12px;border-radius:14px;background:var(--p-list-item-surface);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 72%,transparent 28%)}.expansion-info-title,.expansion-timeline-title{font-size:.92rem;font-weight:600;color:var(--p-list-foreground)}.expansion-info-value,.expansion-timeline-meta,.expansion-timeline-description,.expansion-empty{font-size:.82rem;line-height:1.45;color:var(--p-list-foreground-muted)}.expansion-chip-list{display:flex;flex-wrap:wrap;gap:8px}.expansion-chip{background:var(--p-list-item-surface);border:1px solid var(--md-sys-color-outline-variant)}.expansion-key-value{margin:0}.expansion-key-value-row{display:grid;gap:4px;padding:4px 0 10px;border-bottom:1px solid var(--md-sys-color-outline-variant)}.expansion-key-value-row dt,.expansion-key-value-row dd{margin:0}.expansion-key-value-row dt{font-size:.78rem;font-weight:600;color:var(--p-list-foreground-muted)}.expansion-key-value-row dd{font-size:.9rem;color:var(--p-list-foreground)}.cards-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,max(340px,var(--p-list-tile-minW))),1fr));gap:max(var(--p-list-tile-gap),16px)}.tiles-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(var(--p-list-tile-minW),1fr));gap:var(--p-list-tile-gap)}.item-tile{position:relative;display:flex;flex-direction:column;gap:10px;padding:var(--p-list-tile-padding);border-radius:var(--p-list-tile-radius);background:var(--p-list-surface);border:var(--p-list-border);color:var(--p-list-foreground);cursor:pointer;min-height:160px;transition:box-shadow .16s ease-out,border-color .16s ease-out,transform .12s ease-out,background .16s ease-out}.item-tile:hover{box-shadow:var(--md-sys-elevation-level2);border-color:var(--md-sys-color-outline-variant);background:var(--p-list-item-hover-surface)}.item-tile:active{transform:scale(var(--p-list-tile-press-scale));background:var(--p-list-item-active-surface)}.tile-main-action{display:flex;flex:1;min-width:0;flex-direction:column;gap:10px;color:inherit;cursor:pointer}.tile-main-action:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px;border-radius:calc(var(--p-list-tile-radius) - 4px)}.tile-media{width:100%;aspect-ratio:var(--p-list-tile-media-ratio);border-radius:var(--p-list-tile-media-radius);background:var(--p-list-item-surface);border:1px solid var(--md-sys-color-outline-variant);display:grid;place-items:center;overflow:hidden}.tile-media img{width:100%;height:100%;object-fit:cover;display:block}.tile-media mat-icon{font-size:28px;height:28px;width:28px;color:var(--p-list-foreground-muted)}.tile-body{display:grid;gap:6px;min-width:0}.tile-meta{display:inline-flex;align-items:center;gap:6px;font-size:.75rem;color:var(--p-list-foreground-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tile-trailing{font-size:.75rem;color:var(--p-list-foreground-muted);white-space:nowrap}.tiles-features{display:flex;flex-wrap:wrap;gap:8px;color:inherit}.tile-status{position:absolute;top:10px;right:10px}.tile-actions{position:absolute;top:8px;left:8px;display:flex;gap:4px;opacity:0;pointer-events:none;transform:translateY(-2px);transition:opacity .16s ease-out,transform .16s ease-out}.item-tile:hover .tile-actions,.item-tile:focus-within .tile-actions{opacity:1;pointer-events:auto;transform:translateY(0)}@media(hover:none){.tile-actions{opacity:1;pointer-events:auto;transform:none}}@media(max-width:600px){.tiles-grid{grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}}@media(prefers-reduced-motion:reduce){.item-tile,.tile-actions{transition:none}.item-tile:active{transform:none}.item-card{transition:none}.item-card:active{transform:none}}.item-card{padding:max(var(--p-list-tile-padding),16px);display:flex;flex-direction:column;gap:var(--p-list-item-gap);min-height:220px;position:relative;cursor:pointer;background:var(--p-list-surface);border:var(--p-list-border);border-radius:var(--p-list-tile-radius);box-shadow:var(--p-list-shadow);transition:box-shadow .16s ease-out,border-color .16s ease-out,transform .12s ease-out,background .16s ease-out;--p-list-leading-width: 96px;--p-list-trailing-min-width: 0px}.card-main-action{display:block;width:100%;min-width:0;padding:0;border:0;background:transparent;color:inherit;text-align:start;font:inherit;cursor:pointer}.card-main-action:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px;border-radius:calc(var(--p-list-tile-radius) - 4px)}.item-card .list-item-content{display:block;padding:0;min-width:0;overflow:hidden}.item-card .list-item-leading{position:absolute;top:18px;left:18px;min-height:0;z-index:1}.item-card .leading-placeholder{width:36px;height:36px}.item-card .leading-icon.mat-icon,.item-card .list-item-leading>mat-icon{width:36px;height:36px;line-height:36px;font-size:20px;display:inline-flex;align-items:center;justify-content:center;border-radius:14px;background:linear-gradient(145deg,color-mix(in srgb,var(--md-sys-color-primary) 18%,transparent),color-mix(in srgb,var(--md-sys-color-primary-container) 34%,transparent));color:var(--md-sys-color-primary)}.item-card .list-item-text{display:grid;gap:9px;padding-left:50px;width:100%;min-width:0;box-sizing:border-box}.item-card .primary{padding-right:min(50%,160px);font-size:1rem;font-weight:800;letter-spacing:-.01em;line-height:1.18;min-width:0;overflow-wrap:anywhere}.item-card .secondary{color:var(--p-list-foreground-muted);font-weight:600;line-height:1.35;min-width:0;overflow-wrap:anywhere}.item-card .features{display:flex;flex-direction:column;gap:8px;margin-top:2px}.item-card .feature{display:inline-flex;align-items:start;gap:8px;min-width:0;max-width:100%;overflow-wrap:anywhere}.item-card .feature mat-icon{margin-top:1px;color:var(--p-list-foreground-muted);opacity:.86}.item-card .list-item-trailing{min-width:var(--p-list-trailing-min-width);grid-column:1/-1;align-items:stretch;padding-right:0}.card-detail-strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(148px,1fr));gap:10px;margin-top:16px}.card-detail{min-width:0;padding:11px 12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent);border-radius:14px;background:linear-gradient(145deg,color-mix(in srgb,var(--md-sys-color-primary-container) 22%,transparent),color-mix(in srgb,var(--p-list-surface) 92%,transparent));box-shadow:inset 0 1px #ffffff14}.card-detail__label{display:block;margin-bottom:3px;color:var(--p-list-foreground-muted);font-size:.68rem;font-weight:700;letter-spacing:.08em;line-height:1.1;text-transform:uppercase}.card-detail__value{display:block;min-width:0;color:var(--p-list-foreground);font-size:.94rem;font-weight:800;line-height:1.2;overflow-wrap:normal;word-break:normal}.card-detail__value,.card-detail__value *{white-space:nowrap}.card-detail--balance .card-detail__value{color:var(--md-sys-color-primary)}.card-detail--limit .card-detail__value{color:var(--md-sys-color-error)}.card-detail--risk .card-detail__value{color:var(--md-sys-color-tertiary, var(--p-list-foreground))}.item-card:hover{box-shadow:var(--md-sys-elevation-level3);border-color:var(--md-sys-color-outline-variant);background:var(--p-list-item-hover-surface)}.item-card:active{transform:scale(var(--p-list-tile-press-scale));background:var(--p-list-item-active-surface)}.card-actions{display:flex;align-items:center;justify-content:flex-end;gap:4px;padding:6px 8px 4px;border-top:1px solid var(--md-sys-color-outline-variant);margin-top:auto}.status-overlay{position:absolute;top:14px;right:14px;max-width:min(42%,148px);overflow:hidden;pointer-events:none}.status-overlay .mat-mdc-chip{pointer-events:auto;width:100%;min-width:0;max-width:100%}.status-overlay .mdc-evolution-chip__text-label,.status-overlay .mat-mdc-chip-action-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.leading-icon.mat-icon{width:36px;height:36px;line-height:36px;font-size:20px;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.mat-mdc-chip{--mdc-chip-container-height: var(--p-list-chip-height);font-size:var(--p-list-chip-font-size)}.meta .mat-icon{font-size:18px;height:18px;width:18px}.features{overflow:hidden}@keyframes shimmer{0%{background-position:-468px 0}to{background-position:468px 0}}.skeleton{animation-duration:1.2s;animation-fill-mode:forwards;animation-iteration-count:infinite;animation-name:shimmer;animation-timing-function:linear;background:linear-gradient(to right,var(--md-sys-color-surface-container-low) 8%,var(--md-sys-color-surface-container) 18%,var(--md-sys-color-surface-container-low) 33%);background-size:800px 104px;position:relative}.skeleton-avatar{width:32px;height:32px;border-radius:50%}.skeleton-line{height:10px;margin:6px 0;border-radius:6px}.skeleton-chip{width:48px;height:18px;border-radius:999px}.chip-outlined.mat-mdc-chip{background:transparent!important;border:1px solid currentColor}.w-60{width:60%}.w-40{width:40%}.list-collection-toolbar{display:flex;align-items:center;gap:12px;padding:12px;margin-bottom:8px;color:var(--p-list-foreground);background:var(--p-list-surface-low);border:var(--p-list-border);border-radius:calc(var(--p-list-radius) * .75)}.list-toolbar-search{flex:1 1 320px;min-width:220px;max-width:440px}.list-toolbar-commands{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:8px;margin-inline-start:auto}.list-toolbar-commands button{border-radius:8px}.list-toolbar-commands button .mat-icon{margin-right:6px}.list-toolbar-sort{width:200px}.list-remote-error{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:12px;margin:8px 4px 0;padding:16px;color:var(--md-sys-color-on-error-container);background:var(--md-sys-color-error-container);border-radius:calc(var(--p-list-radius) * .75)}.list-remote-error__copy{display:flex;flex-direction:column;gap:4px;min-width:0}.list-remote-error__copy span{color:color-mix(in srgb,var(--md-sys-color-on-error-container) 82%,transparent);line-height:1.45}.list-remote-error button{color:var(--md-sys-color-on-error-container);--mat-button-outlined-label-text-color: var( --md-sys-color-on-error-container );--mat-button-outlined-outline-color: color-mix( in srgb, var(--md-sys-color-on-error-container) 42%, transparent );--mdc-outlined-button-label-text-color: var( --md-sys-color-on-error-container );--mdc-outlined-button-outline-color: color-mix( in srgb, var(--md-sys-color-on-error-container) 42%, transparent )}.mat-mdc-paginator{margin-top:14px;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--p-list-surface-low);color:var(--p-list-foreground)}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-container{min-height:40px;padding:4px 8px;gap:6px}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-page-size-select{margin-left:8px;width:92px}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-page-size-select .mat-mdc-text-field-wrapper{--mdc-outlined-text-field-container-color: color-mix( in srgb, var(--p-list-surface-low) 92%, var(--md-sys-color-on-surface) 8% );--mdc-outlined-text-field-input-text-color: var(--p-list-foreground);min-height:40px;padding-inline:12px 8px;background:color-mix(in srgb,var(--p-list-surface-low) 92%,var(--md-sys-color-on-surface) 8%)!important;background-color:color-mix(in srgb,var(--p-list-surface-low) 92%,var(--md-sys-color-on-surface) 8%)!important;color:var(--p-list-foreground)!important}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-page-size-select .mat-mdc-form-field-infix{min-height:40px;padding-block:8px;width:auto}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-page-size-select .mat-mdc-select-trigger{min-height:24px;padding-right:2px;color:var(--p-list-foreground)}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-range-label,:host ::ng-deep .mat-mdc-paginator .mat-mdc-select-value-text{font-size:12px;color:var(--p-list-foreground-muted)}.list-paginator-total{margin-top:14px;padding:4px 8px 0;font-size:12px;color:var(--p-list-foreground-muted)}:host ::ng-deep .mat-mdc-paginator.hide-range .mat-mdc-paginator-range-label{display:none}::ng-deep .praxis-list-paginator-select-panel.mat-mdc-select-panel{--mat-select-panel-background-color: var( --md-sys-color-surface-container, #fff );background:var(--md-sys-color-surface-container, #fff)!important;background-color:var(--md-sys-color-surface-container, #fff)!important;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #d7dbe5) 70%,transparent);box-shadow:0 12px 28px color-mix(in srgb,var(--md-sys-color-shadow, #000) 28%,transparent)}::ng-deep .praxis-list-paginator-select-panel .mat-mdc-option{--mat-option-label-text-color: var(--md-sys-color-on-surface, #111827);color:var(--md-sys-color-on-surface, #111827)!important}::ng-deep .praxis-list-paginator-select-panel .mat-mdc-option:hover:not(.mdc-list-item--disabled),::ng-deep .praxis-list-paginator-select-panel .mat-mdc-option.mat-mdc-option-active{--mat-option-hover-state-layer-color: color-mix( in srgb, var(--md-sys-color-primary, #2f7cff) 14%, transparent );background:color-mix(in srgb,var(--md-sys-color-primary, #2f7cff) 14%,transparent)!important;color:var(--md-sys-color-on-surface, #111827)!important}.muted{color:var(--p-list-foreground-muted)}@container (max-width: 720px){.list-item-content{grid-template-columns:var(--p-list-leading-width) minmax(0,1fr)}.list-item-content--row-layout{grid-template-columns:var(--p-list-leading-width) minmax(0,1fr) max-content;grid-auto-flow:row;align-items:start}.list-item-content--row-layout .list-row-slot--leading{grid-column:1;grid-row:1}.list-item-content--row-layout .list-row-slot--primary,.list-item-content--row-layout .list-row-slot--identity{grid-column:2/-1;grid-row:1}.list-item-content--row-layout .list-row-slot--secondary{grid-column:2/-1;grid-row:2}.list-item-content--row-layout :is(.list-row-slot--primary,.list-row-slot--secondary){white-space:normal;overflow-wrap:anywhere}.list-item-content--row-layout .list-row-slot--meta{grid-column:2;grid-row:3;justify-content:flex-start}.list-item-content--row-layout .list-row-slot--trailing{grid-column:3;grid-row:3;justify-content:flex-end}.list-selection-control{padding-left:0}.list-item-content--row-layout :is(.list-row-slot--balance,.list-row-slot--limit,.list-row-slot--risk,.list-row-slot--alerts,.list-row-slot--owner,.list-row-slot--actions,.list-row-slot--expand){grid-column:1/-1}.list-item-trailing{grid-column:1/-1;display:flex;flex-wrap:wrap;justify-content:flex-start;padding-right:0;align-items:center;text-align:start}.list-item-trailing .list-item-actions{margin-inline-start:auto}.item-expansion{margin-left:0;margin-right:0}.list-collection-toolbar{flex-direction:column;align-items:stretch}.list-toolbar-search,.list-toolbar-sort{flex:0 0 auto;width:100%;max-width:none;min-width:0}.list-toolbar-commands{width:100%;margin-inline-start:0}.list-toolbar-sort{flex:1 1 100%}.list-toolbar-commands button{flex:0 0 auto}.list-remote-error{grid-template-columns:auto minmax(0,1fr)}.list-remote-error button{grid-column:1/-1;justify-self:stretch}}@media(max-width:720px){.list-item-content{grid-template-columns:var(--p-list-leading-width) minmax(0,1fr)}.list-item-content--row-layout{grid-template-columns:var(--p-list-leading-width) minmax(0,1fr) max-content;grid-auto-flow:row;align-items:start}.list-item-content--row-layout .list-row-slot--leading{grid-column:1;grid-row:1}.list-item-content--row-layout .list-row-slot--primary,.list-item-content--row-layout .list-row-slot--identity{grid-column:2/-1;grid-row:1}.list-item-content--row-layout .list-row-slot--secondary{grid-column:2/-1;grid-row:2}.list-item-content--row-layout :is(.list-row-slot--primary,.list-row-slot--secondary){white-space:normal;overflow-wrap:anywhere}.list-item-content--row-layout .list-row-slot--meta{grid-column:2;grid-row:3;justify-content:flex-start}.list-item-content--row-layout .list-row-slot--trailing{grid-column:3;grid-row:3;justify-content:flex-end}.list-selection-control{padding-left:0}.list-item-content--row-layout :is(.list-row-slot--balance,.list-row-slot--limit,.list-row-slot--risk,.list-row-slot--alerts,.list-row-slot--owner,.list-row-slot--actions,.list-row-slot--expand){grid-column:1/-1}.list-item-trailing{grid-column:1/-1;display:flex;flex-wrap:wrap;justify-content:flex-start;padding-right:0;align-items:center;text-align:start}.list-item-trailing .list-item-actions{margin-inline-start:auto}.item-expansion{margin-left:0;margin-right:0}.list-collection-toolbar{flex-direction:column;align-items:stretch}.list-toolbar-search,.list-toolbar-sort{flex:0 0 auto;width:100%;max-width:none;min-width:0}.list-toolbar-commands{width:100%;margin-inline-start:0}.list-toolbar-sort{flex:1 1 100%}.list-toolbar-commands button{flex:0 0 auto}.list-remote-error{grid-template-columns:auto minmax(0,1fr)}.list-remote-error button{grid-column:1/-1;justify-self:stretch}}\n"] }]
|
|
15608
15685
|
}], propDecorators: { config: [{
|
|
15609
15686
|
type: Input
|
|
15610
15687
|
}], listId: [{
|
|
@@ -15858,6 +15935,67 @@ function parseInlineStyle(inlineStyle) {
|
|
|
15858
15935
|
return entries.length ? Object.fromEntries(entries) : undefined;
|
|
15859
15936
|
}
|
|
15860
15937
|
|
|
15938
|
+
const PRAXIS_LIST_PORTS = [
|
|
15939
|
+
{
|
|
15940
|
+
id: 'queryContext',
|
|
15941
|
+
label: 'Query Context',
|
|
15942
|
+
direction: 'input',
|
|
15943
|
+
semanticKind: 'query-context',
|
|
15944
|
+
schema: {
|
|
15945
|
+
id: 'PraxisDataQueryContext',
|
|
15946
|
+
kind: 'ts-type',
|
|
15947
|
+
ref: 'PraxisDataQueryContext',
|
|
15948
|
+
},
|
|
15949
|
+
compatibility: {
|
|
15950
|
+
acceptsSemanticKinds: ['query-context'],
|
|
15951
|
+
schemaMode: 'structural',
|
|
15952
|
+
},
|
|
15953
|
+
description: 'Contexto declarativo de filtro, ordenação e paginação aplicado ao runtime remoto da lista.',
|
|
15954
|
+
exposure: { public: true, group: 'data' },
|
|
15955
|
+
},
|
|
15956
|
+
{
|
|
15957
|
+
id: 'itemClick',
|
|
15958
|
+
label: 'Item Click',
|
|
15959
|
+
direction: 'output',
|
|
15960
|
+
semanticKind: 'event',
|
|
15961
|
+
schema: {
|
|
15962
|
+
id: 'ListItemEvent',
|
|
15963
|
+
kind: 'ts-type',
|
|
15964
|
+
ref: 'ListItemEvent',
|
|
15965
|
+
},
|
|
15966
|
+
cardinality: 'stream',
|
|
15967
|
+
description: 'Evento emitido quando o usuário ativa um item da lista.',
|
|
15968
|
+
exposure: { public: true, group: 'interaction' },
|
|
15969
|
+
},
|
|
15970
|
+
{
|
|
15971
|
+
id: 'actionClick',
|
|
15972
|
+
label: 'Action Click',
|
|
15973
|
+
direction: 'output',
|
|
15974
|
+
semanticKind: 'event',
|
|
15975
|
+
schema: {
|
|
15976
|
+
id: 'ListActionEvent',
|
|
15977
|
+
kind: 'ts-type',
|
|
15978
|
+
ref: 'ListActionEvent',
|
|
15979
|
+
},
|
|
15980
|
+
cardinality: 'stream',
|
|
15981
|
+
description: 'Evento emitido quando uma ação declarada do item é acionada.',
|
|
15982
|
+
exposure: { public: true, group: 'interaction' },
|
|
15983
|
+
},
|
|
15984
|
+
{
|
|
15985
|
+
id: 'selectionChange',
|
|
15986
|
+
label: 'Selection Change',
|
|
15987
|
+
direction: 'output',
|
|
15988
|
+
semanticKind: 'event',
|
|
15989
|
+
schema: {
|
|
15990
|
+
id: 'ListSelectionEvent',
|
|
15991
|
+
kind: 'ts-type',
|
|
15992
|
+
ref: 'ListSelectionEvent',
|
|
15993
|
+
},
|
|
15994
|
+
cardinality: 'stream',
|
|
15995
|
+
description: 'Evento canônico de seleção com mode, value, items e ids conforme a configuração runtime.',
|
|
15996
|
+
exposure: { public: true, group: 'selection' },
|
|
15997
|
+
},
|
|
15998
|
+
];
|
|
15861
15999
|
const PRAXIS_LIST_COMPONENT_METADATA = {
|
|
15862
16000
|
id: 'praxis-list',
|
|
15863
16001
|
selector: 'praxis-list',
|
|
@@ -15968,6 +16106,7 @@ const PRAXIS_LIST_COMPONENT_METADATA = {
|
|
|
15968
16106
|
{ name: 'actionClick', type: 'ListActionEvent', description: 'Clique em ação de item' },
|
|
15969
16107
|
{ name: 'selectionChange', type: 'ListSelectionEvent', description: 'Mudança de seleção' },
|
|
15970
16108
|
],
|
|
16109
|
+
ports: PRAXIS_LIST_PORTS,
|
|
15971
16110
|
actions: [
|
|
15972
16111
|
{
|
|
15973
16112
|
id: 'item-click',
|