@meshmakers/octo-ui 3.3.860 → 3.3.870

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.
@@ -18,11 +18,11 @@ import { GridModule } from '@progress/kendo-angular-grid';
18
18
  import * as i5$1 from '@progress/kendo-angular-icons';
19
19
  import { IconsModule, SVGIconModule, SVGIconComponent } from '@progress/kendo-angular-icons';
20
20
  import * as i5 from '@progress/kendo-angular-inputs';
21
- import { InputsModule, TextBoxModule, KENDO_INPUTS } from '@progress/kendo-angular-inputs';
22
- import { searchIcon, sortAscSmallIcon, sortDescSmallIcon, filterClearIcon, chevronRightIcon, chevronDownIcon, downloadIcon, fileIcon, folderIcon, calendarIcon, checkboxCheckedIcon, listUnorderedIcon, arrowRightIcon, arrowLeftIcon, chevronDoubleRightIcon, chevronDoubleLeftIcon, arrowUpIcon, arrowDownIcon, pencilIcon, trashIcon, copyIcon, folderMoreIcon, folderOpenIcon, gearIcon, plusIcon, minusIcon, dollarIcon, infoCircleIcon, eyeIcon, hyperlinkOpenIcon, arrowRotateCcwIcon, locationsIcon, arrowRotateCwIcon, xIcon } from '@progress/kendo-svg-icons';
21
+ import { InputsModule, TextBoxModule, KENDO_INPUTS, SwitchModule } from '@progress/kendo-angular-inputs';
22
+ import { searchIcon, sortAscSmallIcon, sortDescSmallIcon, filterClearIcon, fileIcon, folderIcon, calendarIcon, checkboxCheckedIcon, listUnorderedIcon, chevronRightIcon, chevronDownIcon, downloadIcon, windowIcon, arrowRightIcon, arrowLeftIcon, chevronDoubleRightIcon, chevronDoubleLeftIcon, arrowUpIcon, arrowDownIcon, pencilIcon, trashIcon, copyIcon, folderMoreIcon, folderOpenIcon, gearIcon, plusIcon, minusIcon, dollarIcon, hyperlinkOpenIcon, infoCircleIcon, eyeIcon, arrowRotateCcwIcon, locationsIcon, arrowRotateCwIcon, xIcon, checkCircleIcon, exclamationCircleIcon, xCircleIcon } from '@progress/kendo-svg-icons';
23
23
  import { Subject, firstValueFrom, Subscription, of, forkJoin, takeUntil as takeUntil$1, catchError as catchError$1, map as map$1, defer, from, finalize, switchMap as switchMap$1, startWith } from 'rxjs';
24
24
  import { debounceTime, distinctUntilChanged, map, switchMap, tap, catchError, takeUntil } from 'rxjs/operators';
25
- import { LoaderModule } from '@progress/kendo-angular-indicators';
25
+ import { LoaderModule, BadgeModule } from '@progress/kendo-angular-indicators';
26
26
  import { isCompositeFilterDescriptor } from '@progress/kendo-data-query';
27
27
  import { TreeItemDataTyped } from '@meshmakers/shared-services';
28
28
  import * as i1$3 from 'apollo-angular';
@@ -1476,6 +1476,492 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1476
1476
  }]
1477
1477
  }] });
1478
1478
 
1479
+ /**
1480
+ * Content component for displaying Record and RecordArray attribute values.
1481
+ * Designed to be hosted inside a Kendo Window opened via `WindowService.open()`
1482
+ * — this guarantees the window is mounted at the application root (escaping
1483
+ * any tile/widget stacking context) and is fully resizable, draggable,
1484
+ * minimizable and maximizable like the other dialogs in the studio.
1485
+ *
1486
+ * Inputs are set on the component instance after the window is created:
1487
+ * const ref = windowService.open({ content: RecordDetailDialogComponent, ... });
1488
+ * const c = ref.content.instance as RecordDetailDialogComponent;
1489
+ * c.attributeName = ...; c.value = ...; c.type = ...;
1490
+ */
1491
+ class RecordDetailDialogComponent {
1492
+ attributeName = 'Record';
1493
+ value;
1494
+ type = AttributeValueTypeDto.RecordDto;
1495
+ /**
1496
+ * Emitted when the user-visible state changes in a way the host might want
1497
+ * to react to. Currently unused for closing — the host listens to
1498
+ * `WindowRef.result` for that.
1499
+ */
1500
+ closed = new EventEmitter();
1501
+ singleRecordProperties = [];
1502
+ recordItems = [];
1503
+ searchTerm = '';
1504
+ // Icons
1505
+ fileIcon = fileIcon;
1506
+ folderIcon = folderIcon;
1507
+ calendarIcon = calendarIcon;
1508
+ checkboxCheckedIcon = checkboxCheckedIcon;
1509
+ listUnorderedIcon = listUnorderedIcon;
1510
+ chevronRightIcon = chevronRightIcon;
1511
+ chevronDownIcon = chevronDownIcon;
1512
+ get isRecordArray() {
1513
+ return this.type === AttributeValueTypeDto.RecordArrayDto;
1514
+ }
1515
+ /**
1516
+ * Compute the title text for the host window. The host reads this via
1517
+ * the component instance after construction.
1518
+ */
1519
+ get dialogTitle() {
1520
+ const name = this.formatAttributeName(this.attributeName);
1521
+ if (this.isRecordArray && Array.isArray(this.value)) {
1522
+ return `${name} (${this.value.length} record${this.value.length !== 1 ? 's' : ''})`;
1523
+ }
1524
+ return name;
1525
+ }
1526
+ ngOnInit() {
1527
+ this.recompute();
1528
+ }
1529
+ /**
1530
+ * Recompute derived state. Public so the host can call it after late
1531
+ * input assignment (WindowService.open() instantiates the component
1532
+ * before inputs can be set on its instance).
1533
+ */
1534
+ recompute() {
1535
+ if (this.isRecordArray && Array.isArray(this.value)) {
1536
+ this.recordItems = this.value.map((item, originalIndex) => ({
1537
+ originalIndex,
1538
+ label: this.getRecordLabel(item),
1539
+ properties: this.toRecordProperties(item),
1540
+ expanded: true
1541
+ }));
1542
+ this.singleRecordProperties = [];
1543
+ }
1544
+ else {
1545
+ this.singleRecordProperties = this.toRecordProperties(this.value);
1546
+ this.recordItems = [];
1547
+ }
1548
+ this.searchTerm = '';
1549
+ }
1550
+ /**
1551
+ * Records visible after applying the search filter. With no search term
1552
+ * this is just the full list. The matcher checks the record's label and
1553
+ * every property's key + formatted value (case-insensitive substring).
1554
+ */
1555
+ get visibleRecords() {
1556
+ const q = this.searchTerm.trim().toLowerCase();
1557
+ if (!q)
1558
+ return this.recordItems;
1559
+ return this.recordItems.filter(r => this.recordMatches(r, q));
1560
+ }
1561
+ /**
1562
+ * When search is active, force matching records open so their content is
1563
+ * visible without an extra click. Outside search mode the user's manual
1564
+ * expanded state wins.
1565
+ */
1566
+ isRecordExpanded(record) {
1567
+ if (this.searchTerm.trim())
1568
+ return true;
1569
+ return record.expanded;
1570
+ }
1571
+ onSearchInput(event) {
1572
+ this.searchTerm = event.target.value;
1573
+ }
1574
+ clearSearch() {
1575
+ this.searchTerm = '';
1576
+ }
1577
+ recordMatches(record, q) {
1578
+ if (record.label && record.label.toLowerCase().includes(q))
1579
+ return true;
1580
+ for (const p of record.properties) {
1581
+ if (p.key.toLowerCase().includes(q))
1582
+ return true;
1583
+ if (p.formattedValue.toLowerCase().includes(q))
1584
+ return true;
1585
+ }
1586
+ return false;
1587
+ }
1588
+ toggleRecord(index) {
1589
+ const item = this.recordItems[index];
1590
+ if (item) {
1591
+ item.expanded = !item.expanded;
1592
+ }
1593
+ }
1594
+ expandAll() {
1595
+ for (const item of this.recordItems) {
1596
+ item.expanded = true;
1597
+ }
1598
+ }
1599
+ collapseAll() {
1600
+ for (const item of this.recordItems) {
1601
+ item.expanded = false;
1602
+ }
1603
+ }
1604
+ getTypeIcon(type) {
1605
+ switch (type) {
1606
+ case AttributeValueTypeDto.BooleanDto:
1607
+ return checkboxCheckedIcon;
1608
+ case AttributeValueTypeDto.DateTimeDto:
1609
+ case AttributeValueTypeDto.DateTimeOffsetDto:
1610
+ return calendarIcon;
1611
+ case AttributeValueTypeDto.RecordDto:
1612
+ case AttributeValueTypeDto.RecordArrayDto:
1613
+ return folderIcon;
1614
+ case AttributeValueTypeDto.StringArrayDto:
1615
+ case AttributeValueTypeDto.IntegerArrayDto:
1616
+ case AttributeValueTypeDto.IntArrayDto:
1617
+ return listUnorderedIcon;
1618
+ default:
1619
+ return fileIcon;
1620
+ }
1621
+ }
1622
+ formatTypeName(type) {
1623
+ return type.replace('_DTO', '').replace('DTO', '').replace('_', ' ');
1624
+ }
1625
+ toRecordProperties(obj) {
1626
+ const props = this.getObjectProperties(obj);
1627
+ return props.map(prop => {
1628
+ const type = this.inferType(prop.value);
1629
+ return {
1630
+ key: prop.key,
1631
+ value: prop.value,
1632
+ formattedValue: this.formatValue(prop.value, type),
1633
+ type
1634
+ };
1635
+ });
1636
+ }
1637
+ formatValue(value, type) {
1638
+ if (value === null)
1639
+ return '<null>';
1640
+ if (value === undefined)
1641
+ return '<undefined>';
1642
+ if (value === '')
1643
+ return '<empty>';
1644
+ switch (type) {
1645
+ case AttributeValueTypeDto.BooleanDto:
1646
+ return value ? 'True' : 'False';
1647
+ case AttributeValueTypeDto.DateTimeDto:
1648
+ case AttributeValueTypeDto.DateTimeOffsetDto:
1649
+ try {
1650
+ const date = value instanceof Date ? value : new Date(String(value));
1651
+ return isNaN(date.getTime()) ? String(value) : date.toLocaleString();
1652
+ }
1653
+ catch {
1654
+ return String(value);
1655
+ }
1656
+ case AttributeValueTypeDto.RecordDto:
1657
+ case AttributeValueTypeDto.RecordArrayDto:
1658
+ return JSON.stringify(value, null, 2);
1659
+ case AttributeValueTypeDto.DoubleDto: {
1660
+ const num = Number(value);
1661
+ return isNaN(num) ? String(value) : num.toFixed(2);
1662
+ }
1663
+ default:
1664
+ if (Array.isArray(value)) {
1665
+ return value.length <= 5 ? `[${value.join(', ')}]` : `[${value.slice(0, 5).join(', ')}, ... +${value.length - 5} more]`;
1666
+ }
1667
+ return String(value);
1668
+ }
1669
+ }
1670
+ getRecordLabel(obj) {
1671
+ if (typeof obj !== 'object' || obj === null)
1672
+ return null;
1673
+ const record = obj;
1674
+ // OctoMesh RtRecord format
1675
+ if ('ckRecordId' in record) {
1676
+ return String(record['ckRecordId']);
1677
+ }
1678
+ // Try common name fields
1679
+ for (const key of ['name', 'Name', 'label', 'Label', 'id', 'Id']) {
1680
+ if (key in record && record[key] != null) {
1681
+ return String(record[key]);
1682
+ }
1683
+ }
1684
+ return null;
1685
+ }
1686
+ getObjectProperties(obj) {
1687
+ if (typeof obj !== 'object' || obj === null) {
1688
+ return [];
1689
+ }
1690
+ // OctoMesh RtRecord format: { ckRecordId, attributes: [{ attributeName, value }] }
1691
+ const maybeRecord = obj;
1692
+ if ('ckRecordId' in maybeRecord && 'attributes' in maybeRecord && Array.isArray(maybeRecord['attributes'])) {
1693
+ return maybeRecord['attributes'].map(attr => ({
1694
+ key: attr.attributeName || 'unknown',
1695
+ value: attr.value
1696
+ }));
1697
+ }
1698
+ return Object.keys(maybeRecord).map(key => ({
1699
+ key,
1700
+ value: maybeRecord[key]
1701
+ }));
1702
+ }
1703
+ inferType(value) {
1704
+ if (value === null || value === undefined)
1705
+ return AttributeValueTypeDto.StringDto;
1706
+ if (typeof value === 'boolean')
1707
+ return AttributeValueTypeDto.BooleanDto;
1708
+ if (typeof value === 'number') {
1709
+ return Number.isInteger(value) ? AttributeValueTypeDto.IntegerDto : AttributeValueTypeDto.DoubleDto;
1710
+ }
1711
+ if (typeof value === 'string') {
1712
+ if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value))
1713
+ return AttributeValueTypeDto.DateTimeDto;
1714
+ return AttributeValueTypeDto.StringDto;
1715
+ }
1716
+ if (Array.isArray(value)) {
1717
+ if (value.length > 0 && typeof value[0] === 'object')
1718
+ return AttributeValueTypeDto.RecordArrayDto;
1719
+ if (value.length > 0 && typeof value[0] === 'string')
1720
+ return AttributeValueTypeDto.StringArrayDto;
1721
+ if (value.length > 0 && typeof value[0] === 'number')
1722
+ return AttributeValueTypeDto.IntegerArrayDto;
1723
+ return AttributeValueTypeDto.StringArrayDto;
1724
+ }
1725
+ if (typeof value === 'object')
1726
+ return AttributeValueTypeDto.RecordDto;
1727
+ return AttributeValueTypeDto.StringDto;
1728
+ }
1729
+ formatAttributeName(name) {
1730
+ return name
1731
+ .replace(/([A-Z])/g, ' $1')
1732
+ .replace(/^./, str => str.toUpperCase())
1733
+ .trim();
1734
+ }
1735
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RecordDetailDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1736
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: RecordDetailDialogComponent, isStandalone: true, selector: "mm-record-detail-dialog", inputs: { attributeName: "attributeName", value: "value", type: "type" }, outputs: { closed: "closed" }, ngImport: i0, template: `
1737
+ <div class="record-detail-content">
1738
+ @if (isRecordArray && recordItems.length > 1) {
1739
+ <div class="record-toolbar">
1740
+ <input
1741
+ type="text"
1742
+ class="search-input"
1743
+ [value]="searchTerm"
1744
+ (input)="onSearchInput($event)"
1745
+ placeholder="Search records, keys, values…"
1746
+ aria-label="Search" />
1747
+ @if (searchTerm) {
1748
+ <button
1749
+ type="button"
1750
+ class="search-clear"
1751
+ (click)="clearSearch()"
1752
+ aria-label="Clear search"
1753
+ title="Clear search">×</button>
1754
+ <span class="match-count">
1755
+ {{ visibleRecords.length }} of {{ recordItems.length }}
1756
+ </span>
1757
+ }
1758
+ <span class="toolbar-spacer"></span>
1759
+ <button
1760
+ kendoButton
1761
+ fillMode="flat"
1762
+ [svgIcon]="chevronDownIcon"
1763
+ (click)="expandAll()">
1764
+ Expand All
1765
+ </button>
1766
+ <button
1767
+ kendoButton
1768
+ fillMode="flat"
1769
+ [svgIcon]="chevronRightIcon"
1770
+ (click)="collapseAll()">
1771
+ Collapse All
1772
+ </button>
1773
+ </div>
1774
+ }
1775
+
1776
+ @if (isRecordArray && searchTerm && visibleRecords.length === 0) {
1777
+ <div class="no-matches">No records match "{{ searchTerm }}".</div>
1778
+ } @else if (isRecordArray && visibleRecords.length > 0) {
1779
+ <div class="record-array-list">
1780
+ @for (record of visibleRecords; track record.originalIndex) {
1781
+ <div class="record-array-item">
1782
+ <div class="array-item-header" (click)="toggleRecord(record.originalIndex)">
1783
+ <kendo-svgicon
1784
+ [icon]="isRecordExpanded(record) ? chevronDownIcon : chevronRightIcon"
1785
+ class="record-chevron">
1786
+ </kendo-svgicon>
1787
+ <span class="array-item-index">[{{ record.originalIndex }}]</span>
1788
+ @if (record.label) {
1789
+ <span class="array-item-label">{{ record.label }}</span>
1790
+ }
1791
+ <span class="array-item-summary">
1792
+ {{ record.properties.length }} propert{{ record.properties.length === 1 ? 'y' : 'ies' }}
1793
+ </span>
1794
+ </div>
1795
+ @if (isRecordExpanded(record)) {
1796
+ <kendo-grid [data]="record.properties" [resizable]="true" scrollable="none" class="detail-grid">
1797
+ <kendo-grid-column field="key" title="Property" [width]="180">
1798
+ <ng-template kendoGridCellTemplate let-dataItem="dataItem">
1799
+ <div class="property-name-cell">
1800
+ <kendo-svgicon [icon]="getTypeIcon(dataItem.type)" class="type-icon"></kendo-svgicon>
1801
+ <span class="property-name">{{ dataItem.key }}</span>
1802
+ </div>
1803
+ </ng-template>
1804
+ </kendo-grid-column>
1805
+ <kendo-grid-column field="formattedValue" title="Value">
1806
+ <ng-template kendoGridCellTemplate let-dataItem="dataItem">
1807
+ <span class="property-value" [title]="dataItem.formattedValue">{{ dataItem.formattedValue }}</span>
1808
+ </ng-template>
1809
+ </kendo-grid-column>
1810
+ <kendo-grid-column field="type" title="Type" [width]="120">
1811
+ <ng-template kendoGridCellTemplate let-dataItem="dataItem">
1812
+ <span class="type-badge">{{ formatTypeName(dataItem.type) }}</span>
1813
+ </ng-template>
1814
+ </kendo-grid-column>
1815
+ </kendo-grid>
1816
+ }
1817
+ </div>
1818
+ }
1819
+ </div>
1820
+ } @else {
1821
+ <kendo-grid [data]="singleRecordProperties" [resizable]="true" class="detail-grid">
1822
+ <kendo-grid-column field="key" title="Property" [width]="180">
1823
+ <ng-template kendoGridCellTemplate let-dataItem="dataItem">
1824
+ <div class="property-name-cell">
1825
+ <kendo-svgicon [icon]="getTypeIcon(dataItem.type)" class="type-icon"></kendo-svgicon>
1826
+ <span class="property-name">{{ dataItem.key }}</span>
1827
+ </div>
1828
+ </ng-template>
1829
+ </kendo-grid-column>
1830
+ <kendo-grid-column field="formattedValue" title="Value">
1831
+ <ng-template kendoGridCellTemplate let-dataItem="dataItem">
1832
+ <span class="property-value" [title]="dataItem.formattedValue">{{ dataItem.formattedValue }}</span>
1833
+ </ng-template>
1834
+ </kendo-grid-column>
1835
+ <kendo-grid-column field="type" title="Type" [width]="120">
1836
+ <ng-template kendoGridCellTemplate let-dataItem="dataItem">
1837
+ <span class="type-badge">{{ formatTypeName(dataItem.type) }}</span>
1838
+ </ng-template>
1839
+ </kendo-grid-column>
1840
+ </kendo-grid>
1841
+ }
1842
+ </div>
1843
+ `, isInline: true, styles: [":host{display:flex;flex-direction:column;height:100%;width:100%}.record-detail-content{display:flex;flex-direction:column;height:100%;gap:8px;padding:12px 16px;box-sizing:border-box;overflow:hidden}.record-toolbar{display:flex;align-items:center;gap:8px;flex-shrink:0;padding-bottom:4px;border-bottom:1px solid var(--kendo-color-border, #dee2e6)}.toolbar-spacer{flex:1}.search-input{flex:0 1 220px;min-width:0;padding:4px 8px;font:inherit;color:var(--kendo-color-on-app-surface, #1d1b20);background:color-mix(in srgb,var(--kendo-color-on-app-surface, #1d1b20) 6%,transparent);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:3px;outline:none}.search-input::placeholder{color:var(--kendo-color-subtle, #6c757d)}.search-input:focus{border-color:var(--kendo-color-primary, #0d6efd)}.search-clear{flex-shrink:0;width:22px;height:22px;padding:0;background:transparent;border:none;cursor:pointer;font-size:1.1em;line-height:1;color:var(--kendo-color-subtle, #6c757d);border-radius:3px}.search-clear:hover{color:var(--kendo-color-on-app-surface, #1d1b20);background:color-mix(in srgb,var(--kendo-color-on-app-surface, #1d1b20) 8%,transparent)}.match-count{flex-shrink:0;font-size:.8em;color:var(--kendo-color-subtle, #6c757d);font-style:italic}.no-matches{padding:16px;text-align:center;color:var(--kendo-color-subtle, #6c757d);font-style:italic}.record-array-list{display:flex;flex-direction:column;gap:8px;overflow-y:auto;flex:1;min-height:0}.record-array-item{display:block;flex-shrink:0;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;overflow:hidden}.array-item-header{display:flex;align-items:center;gap:8px;padding:6px 10px;cursor:pointer;-webkit-user-select:none;user-select:none;background:var(--kendo-color-surface-alt, #f8f9fa);transition:background-color .15s ease}.array-item-header:hover{background:color-mix(in srgb,var(--kendo-color-on-app-surface, #1d1b20) 6%,transparent)}.record-chevron{width:14px;height:14px;color:var(--kendo-color-subtle);flex-shrink:0}.array-item-index{font-family:Roboto Mono,monospace;font-size:.8em;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.array-item-label{font-size:.85em;color:var(--kendo-color-on-app-surface, #1d1b20);font-weight:500}.array-item-summary{margin-left:auto;font-size:.75em;color:var(--kendo-color-subtle, #6c757d);font-style:italic}.detail-grid{display:block!important;height:auto!important;border:none;border-top:1px solid var(--kendo-color-border, #dee2e6)}:host ::ng-deep .detail-grid .k-grid-aria-root{height:auto}.property-name-cell{display:flex;align-items:center;gap:8px}.type-icon{width:16px;height:16px;opacity:.7}.property-name{font-weight:500}.property-value{word-break:break-word;white-space:pre-wrap}.type-badge{font-size:.75em;padding:2px 6px;border-radius:3px;text-transform:uppercase;font-weight:500;background:color-mix(in srgb,var(--kendo-color-on-app-surface, #1d1b20) 12%,transparent);color:var(--kendo-color-on-app-surface, #1d1b20)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: GridModule }, { kind: "component", type: i3.GridComponent, selector: "kendo-grid", inputs: ["data", "pageSize", "height", "rowHeight", "adaptiveMode", "detailRowHeight", "skip", "scrollable", "selectable", "sort", "size", "trackBy", "filter", "group", "virtualColumns", "filterable", "sortable", "pageable", "groupable", "gridResizable", "rowReorderable", "navigable", "autoSize", "rowClass", "rowSticky", "rowSelected", "isRowSelectable", "cellSelected", "resizable", "reorderable", "loading", "columnMenu", "hideHeader", "showInactiveTools", "isDetailExpanded", "isGroupExpanded", "dataLayoutMode"], outputs: ["filterChange", "pageChange", "groupChange", "sortChange", "selectionChange", "rowReorder", "dataStateChange", "gridStateChange", "groupExpand", "groupCollapse", "detailExpand", "detailCollapse", "edit", "cancel", "save", "remove", "add", "cellClose", "cellClick", "pdfExport", "excelExport", "columnResize", "columnReorder", "columnVisibilityChange", "columnLockedChange", "columnStickyChange", "scrollBottom", "contentScroll"], exportAs: ["kendoGrid"] }, { kind: "component", type: i3.ColumnComponent, selector: "kendo-grid-column", inputs: ["field", "format", "sortable", "groupable", "editor", "filter", "filterVariant", "filterable", "editable"] }, { kind: "directive", type: i3.CellTemplateDirective, selector: "[kendoGridCellTemplate]" }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i5$1.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }] });
1844
+ }
1845
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RecordDetailDialogComponent, decorators: [{
1846
+ type: Component,
1847
+ args: [{ selector: 'mm-record-detail-dialog', standalone: true, imports: [CommonModule, ButtonModule, GridModule, SVGIconModule], template: `
1848
+ <div class="record-detail-content">
1849
+ @if (isRecordArray && recordItems.length > 1) {
1850
+ <div class="record-toolbar">
1851
+ <input
1852
+ type="text"
1853
+ class="search-input"
1854
+ [value]="searchTerm"
1855
+ (input)="onSearchInput($event)"
1856
+ placeholder="Search records, keys, values…"
1857
+ aria-label="Search" />
1858
+ @if (searchTerm) {
1859
+ <button
1860
+ type="button"
1861
+ class="search-clear"
1862
+ (click)="clearSearch()"
1863
+ aria-label="Clear search"
1864
+ title="Clear search">×</button>
1865
+ <span class="match-count">
1866
+ {{ visibleRecords.length }} of {{ recordItems.length }}
1867
+ </span>
1868
+ }
1869
+ <span class="toolbar-spacer"></span>
1870
+ <button
1871
+ kendoButton
1872
+ fillMode="flat"
1873
+ [svgIcon]="chevronDownIcon"
1874
+ (click)="expandAll()">
1875
+ Expand All
1876
+ </button>
1877
+ <button
1878
+ kendoButton
1879
+ fillMode="flat"
1880
+ [svgIcon]="chevronRightIcon"
1881
+ (click)="collapseAll()">
1882
+ Collapse All
1883
+ </button>
1884
+ </div>
1885
+ }
1886
+
1887
+ @if (isRecordArray && searchTerm && visibleRecords.length === 0) {
1888
+ <div class="no-matches">No records match "{{ searchTerm }}".</div>
1889
+ } @else if (isRecordArray && visibleRecords.length > 0) {
1890
+ <div class="record-array-list">
1891
+ @for (record of visibleRecords; track record.originalIndex) {
1892
+ <div class="record-array-item">
1893
+ <div class="array-item-header" (click)="toggleRecord(record.originalIndex)">
1894
+ <kendo-svgicon
1895
+ [icon]="isRecordExpanded(record) ? chevronDownIcon : chevronRightIcon"
1896
+ class="record-chevron">
1897
+ </kendo-svgicon>
1898
+ <span class="array-item-index">[{{ record.originalIndex }}]</span>
1899
+ @if (record.label) {
1900
+ <span class="array-item-label">{{ record.label }}</span>
1901
+ }
1902
+ <span class="array-item-summary">
1903
+ {{ record.properties.length }} propert{{ record.properties.length === 1 ? 'y' : 'ies' }}
1904
+ </span>
1905
+ </div>
1906
+ @if (isRecordExpanded(record)) {
1907
+ <kendo-grid [data]="record.properties" [resizable]="true" scrollable="none" class="detail-grid">
1908
+ <kendo-grid-column field="key" title="Property" [width]="180">
1909
+ <ng-template kendoGridCellTemplate let-dataItem="dataItem">
1910
+ <div class="property-name-cell">
1911
+ <kendo-svgicon [icon]="getTypeIcon(dataItem.type)" class="type-icon"></kendo-svgicon>
1912
+ <span class="property-name">{{ dataItem.key }}</span>
1913
+ </div>
1914
+ </ng-template>
1915
+ </kendo-grid-column>
1916
+ <kendo-grid-column field="formattedValue" title="Value">
1917
+ <ng-template kendoGridCellTemplate let-dataItem="dataItem">
1918
+ <span class="property-value" [title]="dataItem.formattedValue">{{ dataItem.formattedValue }}</span>
1919
+ </ng-template>
1920
+ </kendo-grid-column>
1921
+ <kendo-grid-column field="type" title="Type" [width]="120">
1922
+ <ng-template kendoGridCellTemplate let-dataItem="dataItem">
1923
+ <span class="type-badge">{{ formatTypeName(dataItem.type) }}</span>
1924
+ </ng-template>
1925
+ </kendo-grid-column>
1926
+ </kendo-grid>
1927
+ }
1928
+ </div>
1929
+ }
1930
+ </div>
1931
+ } @else {
1932
+ <kendo-grid [data]="singleRecordProperties" [resizable]="true" class="detail-grid">
1933
+ <kendo-grid-column field="key" title="Property" [width]="180">
1934
+ <ng-template kendoGridCellTemplate let-dataItem="dataItem">
1935
+ <div class="property-name-cell">
1936
+ <kendo-svgicon [icon]="getTypeIcon(dataItem.type)" class="type-icon"></kendo-svgicon>
1937
+ <span class="property-name">{{ dataItem.key }}</span>
1938
+ </div>
1939
+ </ng-template>
1940
+ </kendo-grid-column>
1941
+ <kendo-grid-column field="formattedValue" title="Value">
1942
+ <ng-template kendoGridCellTemplate let-dataItem="dataItem">
1943
+ <span class="property-value" [title]="dataItem.formattedValue">{{ dataItem.formattedValue }}</span>
1944
+ </ng-template>
1945
+ </kendo-grid-column>
1946
+ <kendo-grid-column field="type" title="Type" [width]="120">
1947
+ <ng-template kendoGridCellTemplate let-dataItem="dataItem">
1948
+ <span class="type-badge">{{ formatTypeName(dataItem.type) }}</span>
1949
+ </ng-template>
1950
+ </kendo-grid-column>
1951
+ </kendo-grid>
1952
+ }
1953
+ </div>
1954
+ `, styles: [":host{display:flex;flex-direction:column;height:100%;width:100%}.record-detail-content{display:flex;flex-direction:column;height:100%;gap:8px;padding:12px 16px;box-sizing:border-box;overflow:hidden}.record-toolbar{display:flex;align-items:center;gap:8px;flex-shrink:0;padding-bottom:4px;border-bottom:1px solid var(--kendo-color-border, #dee2e6)}.toolbar-spacer{flex:1}.search-input{flex:0 1 220px;min-width:0;padding:4px 8px;font:inherit;color:var(--kendo-color-on-app-surface, #1d1b20);background:color-mix(in srgb,var(--kendo-color-on-app-surface, #1d1b20) 6%,transparent);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:3px;outline:none}.search-input::placeholder{color:var(--kendo-color-subtle, #6c757d)}.search-input:focus{border-color:var(--kendo-color-primary, #0d6efd)}.search-clear{flex-shrink:0;width:22px;height:22px;padding:0;background:transparent;border:none;cursor:pointer;font-size:1.1em;line-height:1;color:var(--kendo-color-subtle, #6c757d);border-radius:3px}.search-clear:hover{color:var(--kendo-color-on-app-surface, #1d1b20);background:color-mix(in srgb,var(--kendo-color-on-app-surface, #1d1b20) 8%,transparent)}.match-count{flex-shrink:0;font-size:.8em;color:var(--kendo-color-subtle, #6c757d);font-style:italic}.no-matches{padding:16px;text-align:center;color:var(--kendo-color-subtle, #6c757d);font-style:italic}.record-array-list{display:flex;flex-direction:column;gap:8px;overflow-y:auto;flex:1;min-height:0}.record-array-item{display:block;flex-shrink:0;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;overflow:hidden}.array-item-header{display:flex;align-items:center;gap:8px;padding:6px 10px;cursor:pointer;-webkit-user-select:none;user-select:none;background:var(--kendo-color-surface-alt, #f8f9fa);transition:background-color .15s ease}.array-item-header:hover{background:color-mix(in srgb,var(--kendo-color-on-app-surface, #1d1b20) 6%,transparent)}.record-chevron{width:14px;height:14px;color:var(--kendo-color-subtle);flex-shrink:0}.array-item-index{font-family:Roboto Mono,monospace;font-size:.8em;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.array-item-label{font-size:.85em;color:var(--kendo-color-on-app-surface, #1d1b20);font-weight:500}.array-item-summary{margin-left:auto;font-size:.75em;color:var(--kendo-color-subtle, #6c757d);font-style:italic}.detail-grid{display:block!important;height:auto!important;border:none;border-top:1px solid var(--kendo-color-border, #dee2e6)}:host ::ng-deep .detail-grid .k-grid-aria-root{height:auto}.property-name-cell{display:flex;align-items:center;gap:8px}.type-icon{width:16px;height:16px;opacity:.7}.property-name{font-weight:500}.property-value{word-break:break-word;white-space:pre-wrap}.type-badge{font-size:.75em;padding:2px 6px;border-radius:3px;text-transform:uppercase;font-weight:500;background:color-mix(in srgb,var(--kendo-color-on-app-surface, #1d1b20) 12%,transparent);color:var(--kendo-color-on-app-surface, #1d1b20)}\n"] }]
1955
+ }], propDecorators: { attributeName: [{
1956
+ type: Input
1957
+ }], value: [{
1958
+ type: Input
1959
+ }], type: [{
1960
+ type: Input
1961
+ }], closed: [{
1962
+ type: Output
1963
+ }] } });
1964
+
1479
1965
  /**
1480
1966
  * Component for displaying property values with appropriate formatting
1481
1967
  */
@@ -1483,11 +1969,13 @@ class PropertyValueDisplayComponent {
1483
1969
  value;
1484
1970
  type = AttributeValueTypeDto.StringDto;
1485
1971
  displayMode = PropertyDisplayMode.Text;
1972
+ attributeName = 'Record';
1486
1973
  /** Emitted when a binary download is requested */
1487
1974
  binaryDownload = new EventEmitter();
1488
1975
  // Expansion state
1489
1976
  isExpanded = false;
1490
- // Pre-computed template properties (computed once in ngOnInit)
1977
+ windowService = inject(WindowService);
1978
+ // Pre-computed template properties — recomputed in ngOnInit and ngOnChanges so Kendo Grid row recycling (cell component reuse with new inputs) does not display stale values.
1491
1979
  expandableRecord = false;
1492
1980
  complexType = false;
1493
1981
  binaryLinkedWithDownload = false;
@@ -1499,13 +1987,33 @@ class PropertyValueDisplayComponent {
1499
1987
  binarySize = null;
1500
1988
  binaryContentType = null;
1501
1989
  formattedBinarySize = '';
1990
+ // RecordArray table-mode state — used when the array is uniform enough to be
1991
+ // displayed as a compact table (one row per record, columns = union of keys).
1992
+ // Falls back to the per-item nested-property layout otherwise.
1993
+ useRecordArrayTable = false;
1994
+ recordArrayColumns = [];
1995
+ recordArrayRows = [];
1996
+ /** Hard cap so we don't render a horizontally-unmanageable table. */
1997
+ static MAX_TABLE_COLUMNS = 12;
1502
1998
  PropertyDisplayMode = PropertyDisplayMode;
1503
1999
  AttributeValueTypeDto = AttributeValueTypeDto;
1504
2000
  Array = Array;
1505
2001
  chevronRightIcon = chevronRightIcon;
1506
2002
  chevronDownIcon = chevronDownIcon;
1507
2003
  downloadIcon = downloadIcon;
2004
+ windowIcon = windowIcon;
1508
2005
  ngOnInit() {
2006
+ this.recomputeDerivedValues();
2007
+ }
2008
+ ngOnChanges(changes) {
2009
+ // Collapse any user-driven expansion carried over from a recycled cell
2010
+ // when the underlying data changes; displayMode changes do not qualify.
2011
+ if (changes['value'] || changes['type']) {
2012
+ this.isExpanded = false;
2013
+ }
2014
+ this.recomputeDerivedValues();
2015
+ }
2016
+ recomputeDerivedValues() {
1509
2017
  this.expandableRecord = this.computeIsExpandableRecord();
1510
2018
  this.complexType = this.computeIsComplexType();
1511
2019
  this.binaryLinkedWithDownload = this.computeIsBinaryLinkedWithDownload();
@@ -1513,6 +2021,7 @@ class PropertyValueDisplayComponent {
1513
2021
  this.recordSummary = this.computeRecordSummary();
1514
2022
  this.typeIndicator = this.computeTypeIndicator();
1515
2023
  this.typeDescription = this.computeTypeDescription();
2024
+ this.computeRecordArrayTable();
1516
2025
  if (this.binaryLinkedWithDownload) {
1517
2026
  const bv = this.value;
1518
2027
  this.binaryFilename = bv?.filename || null;
@@ -1520,6 +2029,12 @@ class PropertyValueDisplayComponent {
1520
2029
  this.binaryContentType = bv?.contentType || null;
1521
2030
  this.formattedBinarySize = this.formatFileSize(this.binarySize);
1522
2031
  }
2032
+ else {
2033
+ this.binaryFilename = null;
2034
+ this.binarySize = null;
2035
+ this.binaryContentType = null;
2036
+ this.formattedBinarySize = '';
2037
+ }
1523
2038
  }
1524
2039
  computeFormattedValue() {
1525
2040
  if (this.value === null) {
@@ -1565,6 +2080,44 @@ class PropertyValueDisplayComponent {
1565
2080
  || this.type === AttributeValueTypeDto.RecordArrayDto;
1566
2081
  return isRecordType && this.value != null && typeof this.value === 'object';
1567
2082
  }
2083
+ /**
2084
+ * For RecordArray values, attempt to render as a compact table when the
2085
+ * records share enough structure: ≥ 2 items, every item is object-like, and
2086
+ * the union of keys stays within MAX_TABLE_COLUMNS. Columns are ordered by
2087
+ * first appearance. Rows are stored as plain objects keyed by column name
2088
+ * so the template can do `row[col]` lookups cheaply.
2089
+ */
2090
+ computeRecordArrayTable() {
2091
+ this.useRecordArrayTable = false;
2092
+ this.recordArrayColumns = [];
2093
+ this.recordArrayRows = [];
2094
+ if (this.type !== AttributeValueTypeDto.RecordArrayDto)
2095
+ return;
2096
+ if (!Array.isArray(this.value) || this.value.length < 2)
2097
+ return;
2098
+ if (!this.value.every(item => item != null && typeof item === 'object'))
2099
+ return;
2100
+ const cols = [];
2101
+ const seen = new Set();
2102
+ const rows = [];
2103
+ for (const item of this.value) {
2104
+ const props = this.getObjectProperties(item);
2105
+ const row = {};
2106
+ for (const { key, value } of props) {
2107
+ if (!seen.has(key)) {
2108
+ seen.add(key);
2109
+ cols.push(key);
2110
+ }
2111
+ row[key] = value;
2112
+ }
2113
+ rows.push(row);
2114
+ }
2115
+ if (cols.length === 0 || cols.length > PropertyValueDisplayComponent.MAX_TABLE_COLUMNS)
2116
+ return;
2117
+ this.recordArrayColumns = cols;
2118
+ this.recordArrayRows = rows;
2119
+ this.useRecordArrayTable = true;
2120
+ }
1568
2121
  computeIsComplexType() {
1569
2122
  const isComplexDataType = [
1570
2123
  AttributeValueTypeDto.RecordDto,
@@ -1700,6 +2253,39 @@ class PropertyValueDisplayComponent {
1700
2253
  toggleExpansion() {
1701
2254
  this.isExpanded = !this.isExpanded;
1702
2255
  }
2256
+ /**
2257
+ * Open the record-detail viewer in a Kendo Window. Using `WindowService.open`
2258
+ * (instead of an inline `<kendo-window>`) is what makes the window mount at
2259
+ * the application root with proper viewport positioning. An inline window
2260
+ * inherits its tile's `position: relative` ancestor and ends up anchored
2261
+ * inside that tile's box rather than on the meshboard surface.
2262
+ */
2263
+ openDetailDialog(event) {
2264
+ event.stopPropagation();
2265
+ const ref = this.windowService.open({
2266
+ title: this.computeDialogTitle(),
2267
+ content: RecordDetailDialogComponent,
2268
+ width: 720,
2269
+ height: 560,
2270
+ minWidth: 420,
2271
+ minHeight: 280,
2272
+ });
2273
+ const instance = ref.content.instance;
2274
+ instance.attributeName = this.attributeName;
2275
+ instance.value = this.value;
2276
+ instance.type = this.type;
2277
+ instance.recompute();
2278
+ }
2279
+ computeDialogTitle() {
2280
+ const formatted = this.attributeName
2281
+ .replace(/([A-Z])/g, ' $1')
2282
+ .replace(/^./, s => s.toUpperCase())
2283
+ .trim();
2284
+ if (this.type === AttributeValueTypeDto.RecordArrayDto && Array.isArray(this.value)) {
2285
+ return `${formatted} (${this.value.length} record${this.value.length !== 1 ? 's' : ''})`;
2286
+ }
2287
+ return formatted;
2288
+ }
1703
2289
  /**
1704
2290
  * Compute summary text for record header
1705
2291
  */
@@ -1829,7 +2415,7 @@ class PropertyValueDisplayComponent {
1829
2415
  }
1830
2416
  }
1831
2417
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: PropertyValueDisplayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1832
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: PropertyValueDisplayComponent, isStandalone: true, selector: "mm-property-value-display", inputs: { value: "value", type: "type", displayMode: "displayMode" }, outputs: { binaryDownload: "binaryDownload" }, ngImport: i0, template: `
2418
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: PropertyValueDisplayComponent, isStandalone: true, selector: "mm-property-value-display", inputs: { value: "value", type: "type", displayMode: "displayMode", attributeName: "attributeName" }, outputs: { binaryDownload: "binaryDownload" }, usesOnChanges: true, ngImport: i0, template: `
1833
2419
  <div class="property-value-display" [ngClass]="'type-' + type.toLowerCase()">
1834
2420
 
1835
2421
  @if (expandableRecord) {
@@ -1841,6 +2427,15 @@ class PropertyValueDisplayComponent {
1841
2427
  class="expand-icon">
1842
2428
  </kendo-svgicon>
1843
2429
  <span class="record-summary">{{ recordSummary }}</span>
2430
+ <button
2431
+ kendoButton
2432
+ size="small"
2433
+ fillMode="flat"
2434
+ [svgIcon]="windowIcon"
2435
+ title="Show details"
2436
+ class="detail-button"
2437
+ (click)="openDetailDialog($event)">
2438
+ </button>
1844
2439
  <span class="type-indicator" [title]="typeDescription">
1845
2440
  {{ typeIndicator }}
1846
2441
  </span>
@@ -1849,23 +2444,58 @@ class PropertyValueDisplayComponent {
1849
2444
  <!-- Expanded Content -->
1850
2445
  @if (isExpanded) {
1851
2446
  <div class="record-content">
1852
- @if (type === AttributeValueTypeDto.RecordArrayDto && Array.isArray(value)) {
1853
- @for (item of value; track $index) {
1854
- <div class="array-item">
1855
- <span class="array-index">[{{ $index }}]</span>
1856
- <div class="nested-properties">
1857
- @for (prop of getObjectProperties(item); track prop.key) {
1858
- <div class="nested-property">
1859
- <span class="property-key">{{ prop.key }}:</span>
1860
- <mm-property-value-display
1861
- [value]="prop.value"
1862
- [type]="getPropertyType(prop.value)">
1863
- </mm-property-value-display>
1864
- </div>
2447
+ @if (useRecordArrayTable) {
2448
+ <!-- Uniform record array compact table with sticky header -->
2449
+ <div class="record-table-container">
2450
+ <table class="record-table">
2451
+ <thead>
2452
+ <tr>
2453
+ <th class="row-index">#</th>
2454
+ @for (col of recordArrayColumns; track col) {
2455
+ <th>{{ col }}</th>
2456
+ }
2457
+ </tr>
2458
+ </thead>
2459
+ <tbody>
2460
+ @for (row of recordArrayRows; track $index) {
2461
+ <tr>
2462
+ <td class="row-index">{{ $index }}</td>
2463
+ @for (col of recordArrayColumns; track col) {
2464
+ <td>
2465
+ @let v = row[col];
2466
+ @if (v !== null && v !== undefined) {
2467
+ <mm-property-value-display
2468
+ [value]="v"
2469
+ [type]="getPropertyType(v)">
2470
+ </mm-property-value-display>
2471
+ }
2472
+ </td>
2473
+ }
2474
+ </tr>
1865
2475
  }
2476
+ </tbody>
2477
+ </table>
2478
+ </div>
2479
+ } @else if (type === AttributeValueTypeDto.RecordArrayDto && Array.isArray(value)) {
2480
+ <!-- Heterogeneous array (or single item) — fallback to per-item nesting, capped height -->
2481
+ <div class="record-array-list">
2482
+ @for (item of value; track $index) {
2483
+ <div class="array-item">
2484
+ <span class="array-index">[{{ $index }}]</span>
2485
+ <div class="nested-properties">
2486
+ @for (prop of getObjectProperties(item); track prop.key) {
2487
+ <div class="nested-property">
2488
+ <span class="property-key">{{ prop.key }}:</span>
2489
+ <mm-property-value-display
2490
+ [value]="prop.value"
2491
+ [type]="getPropertyType(prop.value)">
2492
+ </mm-property-value-display>
2493
+ </div>
2494
+ }
2495
+ </div>
1866
2496
  </div>
1867
- </div>
1868
- }
2497
+ }
2498
+ </div>
1869
2499
  } @else {
1870
2500
  <div class="nested-properties">
1871
2501
  @for (prop of getObjectProperties(value); track prop.key) {
@@ -1926,8 +2556,9 @@ class PropertyValueDisplayComponent {
1926
2556
  </span>
1927
2557
  }
1928
2558
  }
2559
+
1929
2560
  </div>
1930
- `, isInline: true, styles: [".property-value-display{display:flex;align-items:flex-start;gap:8px;width:100%;min-width:0}.text-display,.default-display{word-break:break-word;white-space:pre-wrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0}.json-display{font-family:Roboto Mono,monospace;font-size:.8em;white-space:pre-wrap;word-break:break-word;margin:0;flex:1;min-width:0}.type-indicator{font-family:Roboto Mono,monospace;font-size:.7em;padding:1px 5px;background:var(--kendo-color-base-subtle);border-radius:3px;color:var(--kendo-color-subtle);white-space:nowrap;flex-shrink:0}.expandable-record{width:100%}.record-header{display:flex;align-items:center;gap:6px;cursor:pointer;padding:2px 0;border-radius:3px;transition:background-color .15s ease}.record-header:hover{background-color:var(--kendo-color-base-subtle, rgba(0,0,0,.04))}.expand-icon{flex-shrink:0;width:14px;height:14px;color:var(--kendo-color-subtle)}.record-summary{color:var(--kendo-color-subtle);font-size:.85em;font-style:italic;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.record-content{margin-left:20px;padding-left:8px;border-left:1px solid var(--kendo-color-border, #dee2e6)}.array-item{margin:4px 0}.array-index{font-family:Roboto Mono,monospace;font-size:.8em;color:var(--kendo-color-subtle)}.nested-properties{margin-left:8px}.nested-property{display:flex;align-items:flex-start;gap:8px;padding:2px 0}.property-key{font-weight:500;white-space:nowrap;flex-shrink:0;color:var(--kendo-color-subtle);font-size:.85em}.binary-linked-display{display:flex;align-items:center;gap:12px;width:100%}.binary-info{display:flex;align-items:center;gap:8px;flex:1;min-width:0}.filename{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-size{font-size:.85em;color:var(--kendo-color-subtle);flex-shrink:0}.content-type{font-size:.75em;padding:2px 6px;background:var(--kendo-color-base-subtle);border-radius:3px;color:var(--kendo-color-subtle);flex-shrink:0}\n"], dependencies: [{ kind: "component", type: PropertyValueDisplayComponent, selector: "mm-property-value-display", inputs: ["value", "type", "displayMode"], outputs: ["binaryDownload"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i5$1.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2561
+ `, isInline: true, styles: [".property-value-display{display:flex;align-items:flex-start;gap:8px;width:100%;min-width:0}.text-display,.default-display{word-break:break-word;white-space:pre-wrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0}.json-display{font-family:Roboto Mono,monospace;font-size:.8em;white-space:pre-wrap;word-break:break-word;margin:0;flex:1;min-width:0}.type-indicator{font-family:Roboto Mono,monospace;font-size:.7em;padding:1px 5px;background:color-mix(in srgb,var(--kendo-color-on-app-surface, #1d1b20) 12%,transparent);border-radius:3px;color:var(--kendo-color-on-app-surface, #1d1b20);white-space:nowrap;flex-shrink:0}.expandable-record{width:100%}.record-header{display:flex;align-items:center;gap:6px;cursor:pointer;padding:2px 0;border-radius:3px;transition:background-color .15s ease}.record-header:hover{background-color:var(--kendo-color-base-subtle, rgba(0,0,0,.04))}.expand-icon{flex-shrink:0;width:14px;height:14px;color:var(--kendo-color-subtle)}.record-summary{color:var(--kendo-color-subtle);font-size:.85em;font-style:italic;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.detail-button{flex-shrink:0;opacity:.6;transition:opacity .15s ease}.detail-button:hover{opacity:1}.record-content{margin-left:20px;padding-left:8px;border-left:1px solid var(--kendo-color-border, #dee2e6)}.array-item{margin:4px 0}.array-index{font-family:Roboto Mono,monospace;font-size:.8em;color:var(--kendo-color-subtle)}.record-array-list{max-height:320px;overflow-y:auto}.record-table-container{max-height:320px;overflow:auto;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:3px;margin-top:4px}.record-table{width:100%;border-collapse:collapse;font-size:.85em;table-layout:auto}.record-table th{position:sticky;top:0;z-index:1;background:var(--kendo-color-surface-alt, #f8f9fa);color:var(--kendo-color-subtle);text-align:left;padding:4px 8px;border-bottom:1px solid var(--kendo-color-border, #dee2e6);font-weight:500;font-size:.85em;white-space:nowrap}.record-table td{padding:3px 8px;border-bottom:1px solid color-mix(in srgb,var(--kendo-color-on-app-surface, #1d1b20) 8%,transparent);vertical-align:top;word-break:break-word}.record-table tr:last-child td{border-bottom:none}.record-table tr:hover td{background:color-mix(in srgb,var(--kendo-color-on-app-surface, #1d1b20) 6%,transparent)}.record-table .row-index{color:var(--kendo-color-subtle);font-family:Roboto Mono,monospace;font-size:.85em;text-align:right;width:1px;white-space:nowrap}.nested-properties{margin-left:8px}.nested-property{display:flex;align-items:flex-start;gap:8px;padding:2px 0}.property-key{font-weight:500;white-space:nowrap;flex-shrink:0;color:var(--kendo-color-subtle);font-size:.85em}.binary-linked-display{display:flex;align-items:center;gap:12px;width:100%}.binary-info{display:flex;align-items:center;gap:8px;flex:1;min-width:0}.filename{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-size{font-size:.85em;color:var(--kendo-color-subtle);flex-shrink:0}.content-type{font-size:.75em;padding:2px 6px;background:var(--kendo-color-base-subtle);border-radius:3px;color:var(--kendo-color-subtle);flex-shrink:0}\n"], dependencies: [{ kind: "component", type: PropertyValueDisplayComponent, selector: "mm-property-value-display", inputs: ["value", "type", "displayMode", "attributeName"], outputs: ["binaryDownload"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i5$1.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1931
2562
  }
1932
2563
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: PropertyValueDisplayComponent, decorators: [{
1933
2564
  type: Component,
@@ -1943,6 +2574,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1943
2574
  class="expand-icon">
1944
2575
  </kendo-svgicon>
1945
2576
  <span class="record-summary">{{ recordSummary }}</span>
2577
+ <button
2578
+ kendoButton
2579
+ size="small"
2580
+ fillMode="flat"
2581
+ [svgIcon]="windowIcon"
2582
+ title="Show details"
2583
+ class="detail-button"
2584
+ (click)="openDetailDialog($event)">
2585
+ </button>
1946
2586
  <span class="type-indicator" [title]="typeDescription">
1947
2587
  {{ typeIndicator }}
1948
2588
  </span>
@@ -1951,23 +2591,58 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1951
2591
  <!-- Expanded Content -->
1952
2592
  @if (isExpanded) {
1953
2593
  <div class="record-content">
1954
- @if (type === AttributeValueTypeDto.RecordArrayDto && Array.isArray(value)) {
1955
- @for (item of value; track $index) {
1956
- <div class="array-item">
1957
- <span class="array-index">[{{ $index }}]</span>
1958
- <div class="nested-properties">
1959
- @for (prop of getObjectProperties(item); track prop.key) {
1960
- <div class="nested-property">
1961
- <span class="property-key">{{ prop.key }}:</span>
1962
- <mm-property-value-display
1963
- [value]="prop.value"
1964
- [type]="getPropertyType(prop.value)">
1965
- </mm-property-value-display>
1966
- </div>
2594
+ @if (useRecordArrayTable) {
2595
+ <!-- Uniform record array compact table with sticky header -->
2596
+ <div class="record-table-container">
2597
+ <table class="record-table">
2598
+ <thead>
2599
+ <tr>
2600
+ <th class="row-index">#</th>
2601
+ @for (col of recordArrayColumns; track col) {
2602
+ <th>{{ col }}</th>
2603
+ }
2604
+ </tr>
2605
+ </thead>
2606
+ <tbody>
2607
+ @for (row of recordArrayRows; track $index) {
2608
+ <tr>
2609
+ <td class="row-index">{{ $index }}</td>
2610
+ @for (col of recordArrayColumns; track col) {
2611
+ <td>
2612
+ @let v = row[col];
2613
+ @if (v !== null && v !== undefined) {
2614
+ <mm-property-value-display
2615
+ [value]="v"
2616
+ [type]="getPropertyType(v)">
2617
+ </mm-property-value-display>
2618
+ }
2619
+ </td>
2620
+ }
2621
+ </tr>
1967
2622
  }
2623
+ </tbody>
2624
+ </table>
2625
+ </div>
2626
+ } @else if (type === AttributeValueTypeDto.RecordArrayDto && Array.isArray(value)) {
2627
+ <!-- Heterogeneous array (or single item) — fallback to per-item nesting, capped height -->
2628
+ <div class="record-array-list">
2629
+ @for (item of value; track $index) {
2630
+ <div class="array-item">
2631
+ <span class="array-index">[{{ $index }}]</span>
2632
+ <div class="nested-properties">
2633
+ @for (prop of getObjectProperties(item); track prop.key) {
2634
+ <div class="nested-property">
2635
+ <span class="property-key">{{ prop.key }}:</span>
2636
+ <mm-property-value-display
2637
+ [value]="prop.value"
2638
+ [type]="getPropertyType(prop.value)">
2639
+ </mm-property-value-display>
2640
+ </div>
2641
+ }
2642
+ </div>
1968
2643
  </div>
1969
- </div>
1970
- }
2644
+ }
2645
+ </div>
1971
2646
  } @else {
1972
2647
  <div class="nested-properties">
1973
2648
  @for (prop of getObjectProperties(value); track prop.key) {
@@ -2028,14 +2703,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
2028
2703
  </span>
2029
2704
  }
2030
2705
  }
2706
+
2031
2707
  </div>
2032
- `, styles: [".property-value-display{display:flex;align-items:flex-start;gap:8px;width:100%;min-width:0}.text-display,.default-display{word-break:break-word;white-space:pre-wrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0}.json-display{font-family:Roboto Mono,monospace;font-size:.8em;white-space:pre-wrap;word-break:break-word;margin:0;flex:1;min-width:0}.type-indicator{font-family:Roboto Mono,monospace;font-size:.7em;padding:1px 5px;background:var(--kendo-color-base-subtle);border-radius:3px;color:var(--kendo-color-subtle);white-space:nowrap;flex-shrink:0}.expandable-record{width:100%}.record-header{display:flex;align-items:center;gap:6px;cursor:pointer;padding:2px 0;border-radius:3px;transition:background-color .15s ease}.record-header:hover{background-color:var(--kendo-color-base-subtle, rgba(0,0,0,.04))}.expand-icon{flex-shrink:0;width:14px;height:14px;color:var(--kendo-color-subtle)}.record-summary{color:var(--kendo-color-subtle);font-size:.85em;font-style:italic;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.record-content{margin-left:20px;padding-left:8px;border-left:1px solid var(--kendo-color-border, #dee2e6)}.array-item{margin:4px 0}.array-index{font-family:Roboto Mono,monospace;font-size:.8em;color:var(--kendo-color-subtle)}.nested-properties{margin-left:8px}.nested-property{display:flex;align-items:flex-start;gap:8px;padding:2px 0}.property-key{font-weight:500;white-space:nowrap;flex-shrink:0;color:var(--kendo-color-subtle);font-size:.85em}.binary-linked-display{display:flex;align-items:center;gap:12px;width:100%}.binary-info{display:flex;align-items:center;gap:8px;flex:1;min-width:0}.filename{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-size{font-size:.85em;color:var(--kendo-color-subtle);flex-shrink:0}.content-type{font-size:.75em;padding:2px 6px;background:var(--kendo-color-base-subtle);border-radius:3px;color:var(--kendo-color-subtle);flex-shrink:0}\n"] }]
2708
+ `, styles: [".property-value-display{display:flex;align-items:flex-start;gap:8px;width:100%;min-width:0}.text-display,.default-display{word-break:break-word;white-space:pre-wrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0}.json-display{font-family:Roboto Mono,monospace;font-size:.8em;white-space:pre-wrap;word-break:break-word;margin:0;flex:1;min-width:0}.type-indicator{font-family:Roboto Mono,monospace;font-size:.7em;padding:1px 5px;background:color-mix(in srgb,var(--kendo-color-on-app-surface, #1d1b20) 12%,transparent);border-radius:3px;color:var(--kendo-color-on-app-surface, #1d1b20);white-space:nowrap;flex-shrink:0}.expandable-record{width:100%}.record-header{display:flex;align-items:center;gap:6px;cursor:pointer;padding:2px 0;border-radius:3px;transition:background-color .15s ease}.record-header:hover{background-color:var(--kendo-color-base-subtle, rgba(0,0,0,.04))}.expand-icon{flex-shrink:0;width:14px;height:14px;color:var(--kendo-color-subtle)}.record-summary{color:var(--kendo-color-subtle);font-size:.85em;font-style:italic;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.detail-button{flex-shrink:0;opacity:.6;transition:opacity .15s ease}.detail-button:hover{opacity:1}.record-content{margin-left:20px;padding-left:8px;border-left:1px solid var(--kendo-color-border, #dee2e6)}.array-item{margin:4px 0}.array-index{font-family:Roboto Mono,monospace;font-size:.8em;color:var(--kendo-color-subtle)}.record-array-list{max-height:320px;overflow-y:auto}.record-table-container{max-height:320px;overflow:auto;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:3px;margin-top:4px}.record-table{width:100%;border-collapse:collapse;font-size:.85em;table-layout:auto}.record-table th{position:sticky;top:0;z-index:1;background:var(--kendo-color-surface-alt, #f8f9fa);color:var(--kendo-color-subtle);text-align:left;padding:4px 8px;border-bottom:1px solid var(--kendo-color-border, #dee2e6);font-weight:500;font-size:.85em;white-space:nowrap}.record-table td{padding:3px 8px;border-bottom:1px solid color-mix(in srgb,var(--kendo-color-on-app-surface, #1d1b20) 8%,transparent);vertical-align:top;word-break:break-word}.record-table tr:last-child td{border-bottom:none}.record-table tr:hover td{background:color-mix(in srgb,var(--kendo-color-on-app-surface, #1d1b20) 6%,transparent)}.record-table .row-index{color:var(--kendo-color-subtle);font-family:Roboto Mono,monospace;font-size:.85em;text-align:right;width:1px;white-space:nowrap}.nested-properties{margin-left:8px}.nested-property{display:flex;align-items:flex-start;gap:8px;padding:2px 0}.property-key{font-weight:500;white-space:nowrap;flex-shrink:0;color:var(--kendo-color-subtle);font-size:.85em}.binary-linked-display{display:flex;align-items:center;gap:12px;width:100%}.binary-info{display:flex;align-items:center;gap:8px;flex:1;min-width:0}.filename{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-size{font-size:.85em;color:var(--kendo-color-subtle);flex-shrink:0}.content-type{font-size:.75em;padding:2px 6px;background:var(--kendo-color-base-subtle);border-radius:3px;color:var(--kendo-color-subtle);flex-shrink:0}\n"] }]
2033
2709
  }], propDecorators: { value: [{
2034
2710
  type: Input
2035
2711
  }], type: [{
2036
2712
  type: Input
2037
2713
  }], displayMode: [{
2038
2714
  type: Input
2715
+ }], attributeName: [{
2716
+ type: Input
2039
2717
  }], binaryDownload: [{
2040
2718
  type: Output
2041
2719
  }] } });
@@ -2243,6 +2921,7 @@ class PropertyGridComponent {
2243
2921
  <mm-property-value-display
2244
2922
  [value]="dataItem.value"
2245
2923
  [type]="dataItem.type"
2924
+ [attributeName]="dataItem.displayName || dataItem.name"
2246
2925
  (binaryDownload)="onBinaryDownload($event)">
2247
2926
  </mm-property-value-display>
2248
2927
  </ng-template>
@@ -2265,7 +2944,7 @@ class PropertyGridComponent {
2265
2944
 
2266
2945
  </kendo-grid>
2267
2946
  </div>
2268
- `, isInline: true, styles: [".mm-property-grid{display:flex;flex-direction:column;border:1px solid var(--kendo-color-border);border-radius:4px;overflow:hidden}.search-toolbar{padding:8px;border-bottom:1px solid var(--kendo-color-border);background:var(--kendo-color-base-subtle)}.property-grid{flex:1;border:none}.property-name-cell{display:flex;align-items:center;gap:8px;min-height:24px}.type-icon{width:16px;height:16px;opacity:.7}.property-info{display:flex;align-items:center;gap:4px;flex:1}.property-name{font-weight:500}.required-indicator{color:var(--kendo-color-error);font-weight:700}.readonly-indicator{font-size:12px;opacity:.7}.property-editor{width:100%;min-width:200px}.validation-error{color:var(--kendo-color-error);font-size:.8em;margin-top:4px}.type-badge{font-size:.75em;padding:2px 6px;border-radius:3px;text-transform:uppercase;font-weight:500;background:var(--kendo-color-base-subtle);color:var(--kendo-color-on-base)}.grid-toolbar{padding:8px;border-top:1px solid var(--kendo-color-border);background:var(--kendo-color-base-subtle);display:flex;align-items:center;gap:8px}.changes-indicator{margin-left:auto;font-size:.9em;color:var(--kendo-color-primary);font-style:italic}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: GridModule }, { kind: "component", type: i3.GridComponent, selector: "kendo-grid", inputs: ["data", "pageSize", "height", "rowHeight", "adaptiveMode", "detailRowHeight", "skip", "scrollable", "selectable", "sort", "size", "trackBy", "filter", "group", "virtualColumns", "filterable", "sortable", "pageable", "groupable", "gridResizable", "rowReorderable", "navigable", "autoSize", "rowClass", "rowSticky", "rowSelected", "isRowSelectable", "cellSelected", "resizable", "reorderable", "loading", "columnMenu", "hideHeader", "showInactiveTools", "isDetailExpanded", "isGroupExpanded", "dataLayoutMode"], outputs: ["filterChange", "pageChange", "groupChange", "sortChange", "selectionChange", "rowReorder", "dataStateChange", "gridStateChange", "groupExpand", "groupCollapse", "detailExpand", "detailCollapse", "edit", "cancel", "save", "remove", "add", "cellClose", "cellClick", "pdfExport", "excelExport", "columnResize", "columnReorder", "columnVisibilityChange", "columnLockedChange", "columnStickyChange", "scrollBottom", "contentScroll"], exportAs: ["kendoGrid"] }, { kind: "component", type: i3.ColumnComponent, selector: "kendo-grid-column", inputs: ["field", "format", "sortable", "groupable", "editor", "filter", "filterVariant", "filterable", "editable"] }, { kind: "directive", type: i3.CellTemplateDirective, selector: "[kendoGridCellTemplate]" }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "ngmodule", type: ButtonsModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i5$1.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: PropertyValueDisplayComponent, selector: "mm-property-value-display", inputs: ["value", "type", "displayMode"], outputs: ["binaryDownload"] }] });
2947
+ `, isInline: true, styles: [".mm-property-grid{display:flex;flex-direction:column;border:1px solid var(--kendo-color-border);border-radius:4px;overflow:hidden}.search-toolbar{padding:8px;border-bottom:1px solid var(--kendo-color-border);background:var(--kendo-color-base-subtle)}.property-grid{flex:1;border:none}.property-name-cell{display:flex;align-items:center;gap:8px;min-height:24px}.type-icon{width:16px;height:16px;opacity:.7}.property-info{display:flex;align-items:center;gap:4px;flex:1}.property-name{font-weight:500}.required-indicator{color:var(--kendo-color-error);font-weight:700}.readonly-indicator{font-size:12px;opacity:.7}.property-editor{width:100%;min-width:200px}.validation-error{color:var(--kendo-color-error);font-size:.8em;margin-top:4px}.type-badge{font-size:.75em;padding:2px 6px;border-radius:3px;text-transform:uppercase;font-weight:500;background:var(--kendo-color-base-subtle);color:var(--kendo-color-on-base)}.grid-toolbar{padding:8px;border-top:1px solid var(--kendo-color-border);background:var(--kendo-color-base-subtle);display:flex;align-items:center;gap:8px}.changes-indicator{margin-left:auto;font-size:.9em;color:var(--kendo-color-primary);font-style:italic}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: GridModule }, { kind: "component", type: i3.GridComponent, selector: "kendo-grid", inputs: ["data", "pageSize", "height", "rowHeight", "adaptiveMode", "detailRowHeight", "skip", "scrollable", "selectable", "sort", "size", "trackBy", "filter", "group", "virtualColumns", "filterable", "sortable", "pageable", "groupable", "gridResizable", "rowReorderable", "navigable", "autoSize", "rowClass", "rowSticky", "rowSelected", "isRowSelectable", "cellSelected", "resizable", "reorderable", "loading", "columnMenu", "hideHeader", "showInactiveTools", "isDetailExpanded", "isGroupExpanded", "dataLayoutMode"], outputs: ["filterChange", "pageChange", "groupChange", "sortChange", "selectionChange", "rowReorder", "dataStateChange", "gridStateChange", "groupExpand", "groupCollapse", "detailExpand", "detailCollapse", "edit", "cancel", "save", "remove", "add", "cellClose", "cellClick", "pdfExport", "excelExport", "columnResize", "columnReorder", "columnVisibilityChange", "columnLockedChange", "columnStickyChange", "scrollBottom", "contentScroll"], exportAs: ["kendoGrid"] }, { kind: "component", type: i3.ColumnComponent, selector: "kendo-grid-column", inputs: ["field", "format", "sortable", "groupable", "editor", "filter", "filterVariant", "filterable", "editable"] }, { kind: "directive", type: i3.CellTemplateDirective, selector: "[kendoGridCellTemplate]" }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "ngmodule", type: ButtonsModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i5$1.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: PropertyValueDisplayComponent, selector: "mm-property-value-display", inputs: ["value", "type", "displayMode", "attributeName"], outputs: ["binaryDownload"] }] });
2269
2948
  }
2270
2949
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: PropertyGridComponent, decorators: [{
2271
2950
  type: Component,
@@ -2338,6 +3017,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
2338
3017
  <mm-property-value-display
2339
3018
  [value]="dataItem.value"
2340
3019
  [type]="dataItem.type"
3020
+ [attributeName]="dataItem.displayName || dataItem.name"
2341
3021
  (binaryDownload)="onBinaryDownload($event)">
2342
3022
  </mm-property-value-display>
2343
3023
  </ng-template>
@@ -7128,21 +7808,45 @@ const DEFAULT_RUNTIME_BROWSER_MESSAGES = {
7128
7808
  class DataMappingListComponent {
7129
7809
  mappings = [];
7130
7810
  sourceDataPoints = [];
7811
+ /**
7812
+ * Optional expression validator function. When provided, expressions are validated
7813
+ * on change and feedback (error or preview) is shown below the expression field.
7814
+ * The host app can provide an implementation using any expression engine
7815
+ * (e.g., ExpressionEvaluatorService from octo-process-diagrams).
7816
+ */
7817
+ expressionValidator;
7131
7818
  addMapping = new EventEmitter();
7132
7819
  removeMapping = new EventEmitter();
7133
7820
  selectTarget = new EventEmitter();
7134
7821
  selectSourceAttribute = new EventEmitter();
7135
7822
  selectTargetAttribute = new EventEmitter();
7136
7823
  mappingChanged = new EventEmitter();
7824
+ navigateToTarget = new EventEmitter();
7825
+ saveAll = new EventEmitter();
7137
7826
  onSourceDataPointChange(mapping, value) {
7138
7827
  mapping.sourceAttributePath = value;
7139
7828
  this.mappingChanged.emit(mapping);
7140
7829
  }
7141
- saveAll = new EventEmitter();
7830
+ onExpressionChange(mapping, expression) {
7831
+ mapping.mappingExpression = expression;
7832
+ if (this.expressionValidator && expression && expression.trim() !== '') {
7833
+ const result = this.expressionValidator(expression);
7834
+ mapping._expressionValid = result.valid;
7835
+ mapping._expressionError = result.error;
7836
+ mapping._expressionPreview = result.preview;
7837
+ }
7838
+ else {
7839
+ mapping._expressionValid = undefined;
7840
+ mapping._expressionError = undefined;
7841
+ mapping._expressionPreview = undefined;
7842
+ }
7843
+ this.mappingChanged.emit(mapping);
7844
+ }
7142
7845
  plusIcon = plusIcon;
7143
7846
  trashIcon = trashIcon;
7847
+ linkIcon = hyperlinkOpenIcon;
7144
7848
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataMappingListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7145
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DataMappingListComponent, isStandalone: true, selector: "mm-data-mapping-list", inputs: { mappings: "mappings", sourceDataPoints: "sourceDataPoints" }, outputs: { addMapping: "addMapping", removeMapping: "removeMapping", selectTarget: "selectTarget", selectSourceAttribute: "selectSourceAttribute", selectTargetAttribute: "selectTargetAttribute", mappingChanged: "mappingChanged", saveAll: "saveAll" }, ngImport: i0, template: `
7849
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DataMappingListComponent, isStandalone: true, selector: "mm-data-mapping-list", inputs: { mappings: "mappings", sourceDataPoints: "sourceDataPoints", expressionValidator: "expressionValidator" }, outputs: { addMapping: "addMapping", removeMapping: "removeMapping", selectTarget: "selectTarget", selectSourceAttribute: "selectSourceAttribute", selectTargetAttribute: "selectTargetAttribute", mappingChanged: "mappingChanged", navigateToTarget: "navigateToTarget", saveAll: "saveAll" }, ngImport: i0, template: `
7146
7850
  <div class="mapping-list">
7147
7851
  <div class="mapping-toolbar">
7148
7852
  <button kendoButton themeColor="primary" size="small" [svgIcon]="plusIcon"
@@ -7184,16 +7888,43 @@ class DataMappingListComponent {
7184
7888
  <label>Expression</label>
7185
7889
  <kendo-textbox [(value)]="mapping.mappingExpression"
7186
7890
  placeholder="e.g. value > 0 ? value : 0"
7187
- (valueChange)="mappingChanged.emit(mapping)">
7891
+ (valueChange)="onExpressionChange(mapping, $event)">
7188
7892
  </kendo-textbox>
7893
+ @if (mapping.mappingExpression && expressionValidator) {
7894
+ @if (mapping._expressionValid === false) {
7895
+ <div class="expression-feedback expression-error">{{ mapping._expressionError }}</div>
7896
+ } @else if (mapping._expressionValid === true && mapping._expressionPreview) {
7897
+ <div class="expression-feedback expression-success">value=42 → {{ mapping._expressionPreview }}</div>
7898
+ }
7899
+ }
7189
7900
  </div>
7190
7901
  <div class="mapping-row">
7191
7902
  <label>Target Entity</label>
7192
- <div class="target-display">
7193
- <span class="target-info">{{ mapping.targetCkTypeId || '(not set)' }} {{ mapping.targetName || '' }}</span>
7194
- <button kendoButton fillMode="flat" size="small"
7195
- (click)="selectTarget.emit(mapping)">Select...</button>
7196
- </div>
7903
+ @if (mapping.targetRtId) {
7904
+ <div class="entity-info-display">
7905
+ <div class="entity-info-main">
7906
+ <span class="entity-name">{{ mapping.targetName || mapping.targetRtId }}</span>
7907
+ @if (mapping.targetRtId) {
7908
+ <button kendoButton fillMode="flat" size="small" [svgIcon]="linkIcon"
7909
+ title="Navigate to entity"
7910
+ (click)="navigateToTarget.emit(mapping)"></button>
7911
+ }
7912
+ </div>
7913
+ <div class="entity-info-details">
7914
+ <span class="entity-detail-item">{{ mapping.targetCkTypeId }}</span>
7915
+ <span class="entity-detail-separator">@</span>
7916
+ <span class="entity-detail-item">{{ mapping.targetRtId }}</span>
7917
+ </div>
7918
+ <button kendoButton fillMode="flat" size="small"
7919
+ (click)="selectTarget.emit(mapping)">Change...</button>
7920
+ </div>
7921
+ } @else {
7922
+ <div class="target-display">
7923
+ <span class="target-info">(not set)</span>
7924
+ <button kendoButton fillMode="flat" size="small"
7925
+ (click)="selectTarget.emit(mapping)">Select...</button>
7926
+ </div>
7927
+ }
7197
7928
  </div>
7198
7929
  <div class="mapping-row">
7199
7930
  <label>Target Attribute</label>
@@ -7216,7 +7947,7 @@ class DataMappingListComponent {
7216
7947
  </div>
7217
7948
  }
7218
7949
  </div>
7219
- `, isInline: true, styles: [".mapping-list{display:flex;flex-direction:column;gap:12px}.mapping-toolbar{display:flex;justify-content:flex-end}.mapping-empty-hint{text-align:center;padding:16px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem}.mapping-card{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:6px;overflow:hidden}.mapping-card-header{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-on-primary, #ffffff);background:var(--kendo-color-primary, #ff6358)}.mapping-card-header .mapping-name{flex:1}.mapping-card-body{padding:10px 12px;display:flex;flex-direction:column;gap:8px}.mapping-row{display:flex;flex-direction:column;gap:3px}.mapping-row label{font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d)}.target-display{display:flex;align-items:center;gap:8px;padding:4px 8px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa);min-height:30px}.target-display .target-info{flex:1;font-size:.85rem;font-family:monospace}.mapping-actions{display:flex;justify-content:flex-end;padding-top:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: DropDownListModule }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "ngmodule", type: TextBoxModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "ngmodule", type: SVGIconModule }] });
7950
+ `, isInline: true, styles: [".mapping-list{display:flex;flex-direction:column;gap:12px}.mapping-toolbar{display:flex;justify-content:flex-end}.mapping-empty-hint{text-align:center;padding:16px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem}.mapping-card{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:6px;overflow:hidden}.mapping-card-header{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-on-primary, #ffffff);background:var(--kendo-color-primary, #ff6358)}.mapping-card-header .mapping-name{flex:1}.mapping-card-body{padding:10px 12px;display:flex;flex-direction:column;gap:8px}.mapping-row{display:flex;flex-direction:column;gap:3px}.mapping-row label{font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d)}.target-display{display:flex;align-items:center;gap:8px;padding:4px 8px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa);min-height:30px}.target-display .target-info{flex:1;font-size:.85rem;font-family:monospace}.mapping-actions{display:flex;justify-content:flex-end;padding-top:4px}.entity-info-display{display:flex;flex-direction:column;gap:2px;padding:4px 8px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa)}.entity-info-main{display:flex;align-items:center;gap:4px}.entity-name{flex:1;font-size:.85rem;font-weight:600}.entity-info-details{display:flex;align-items:center;gap:2px;font-size:.7rem;font-family:monospace;color:var(--kendo-color-subtle, #6c757d)}.entity-detail-separator{color:var(--kendo-color-subtle, #6c757d)}.expression-feedback{font-size:.75rem;padding:2px 0;font-family:monospace}.expression-error{color:var(--kendo-color-error, #dc3545)}.expression-success{color:var(--kendo-color-success, #28a745)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: DropDownListModule }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "ngmodule", type: TextBoxModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "ngmodule", type: SVGIconModule }] });
7220
7951
  }
7221
7952
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataMappingListComponent, decorators: [{
7222
7953
  type: Component,
@@ -7269,16 +8000,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
7269
8000
  <label>Expression</label>
7270
8001
  <kendo-textbox [(value)]="mapping.mappingExpression"
7271
8002
  placeholder="e.g. value > 0 ? value : 0"
7272
- (valueChange)="mappingChanged.emit(mapping)">
8003
+ (valueChange)="onExpressionChange(mapping, $event)">
7273
8004
  </kendo-textbox>
8005
+ @if (mapping.mappingExpression && expressionValidator) {
8006
+ @if (mapping._expressionValid === false) {
8007
+ <div class="expression-feedback expression-error">{{ mapping._expressionError }}</div>
8008
+ } @else if (mapping._expressionValid === true && mapping._expressionPreview) {
8009
+ <div class="expression-feedback expression-success">value=42 → {{ mapping._expressionPreview }}</div>
8010
+ }
8011
+ }
7274
8012
  </div>
7275
8013
  <div class="mapping-row">
7276
8014
  <label>Target Entity</label>
7277
- <div class="target-display">
7278
- <span class="target-info">{{ mapping.targetCkTypeId || '(not set)' }} {{ mapping.targetName || '' }}</span>
7279
- <button kendoButton fillMode="flat" size="small"
7280
- (click)="selectTarget.emit(mapping)">Select...</button>
7281
- </div>
8015
+ @if (mapping.targetRtId) {
8016
+ <div class="entity-info-display">
8017
+ <div class="entity-info-main">
8018
+ <span class="entity-name">{{ mapping.targetName || mapping.targetRtId }}</span>
8019
+ @if (mapping.targetRtId) {
8020
+ <button kendoButton fillMode="flat" size="small" [svgIcon]="linkIcon"
8021
+ title="Navigate to entity"
8022
+ (click)="navigateToTarget.emit(mapping)"></button>
8023
+ }
8024
+ </div>
8025
+ <div class="entity-info-details">
8026
+ <span class="entity-detail-item">{{ mapping.targetCkTypeId }}</span>
8027
+ <span class="entity-detail-separator">@</span>
8028
+ <span class="entity-detail-item">{{ mapping.targetRtId }}</span>
8029
+ </div>
8030
+ <button kendoButton fillMode="flat" size="small"
8031
+ (click)="selectTarget.emit(mapping)">Change...</button>
8032
+ </div>
8033
+ } @else {
8034
+ <div class="target-display">
8035
+ <span class="target-info">(not set)</span>
8036
+ <button kendoButton fillMode="flat" size="small"
8037
+ (click)="selectTarget.emit(mapping)">Select...</button>
8038
+ </div>
8039
+ }
7282
8040
  </div>
7283
8041
  <div class="mapping-row">
7284
8042
  <label>Target Attribute</label>
@@ -7301,11 +8059,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
7301
8059
  </div>
7302
8060
  }
7303
8061
  </div>
7304
- `, styles: [".mapping-list{display:flex;flex-direction:column;gap:12px}.mapping-toolbar{display:flex;justify-content:flex-end}.mapping-empty-hint{text-align:center;padding:16px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem}.mapping-card{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:6px;overflow:hidden}.mapping-card-header{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-on-primary, #ffffff);background:var(--kendo-color-primary, #ff6358)}.mapping-card-header .mapping-name{flex:1}.mapping-card-body{padding:10px 12px;display:flex;flex-direction:column;gap:8px}.mapping-row{display:flex;flex-direction:column;gap:3px}.mapping-row label{font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d)}.target-display{display:flex;align-items:center;gap:8px;padding:4px 8px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa);min-height:30px}.target-display .target-info{flex:1;font-size:.85rem;font-family:monospace}.mapping-actions{display:flex;justify-content:flex-end;padding-top:4px}\n"] }]
8062
+ `, styles: [".mapping-list{display:flex;flex-direction:column;gap:12px}.mapping-toolbar{display:flex;justify-content:flex-end}.mapping-empty-hint{text-align:center;padding:16px;color:var(--kendo-color-subtle, #6c757d);font-size:.85rem}.mapping-card{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:6px;overflow:hidden}.mapping-card-header{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-on-primary, #ffffff);background:var(--kendo-color-primary, #ff6358)}.mapping-card-header .mapping-name{flex:1}.mapping-card-body{padding:10px 12px;display:flex;flex-direction:column;gap:8px}.mapping-row{display:flex;flex-direction:column;gap:3px}.mapping-row label{font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d)}.target-display{display:flex;align-items:center;gap:8px;padding:4px 8px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa);min-height:30px}.target-display .target-info{flex:1;font-size:.85rem;font-family:monospace}.mapping-actions{display:flex;justify-content:flex-end;padding-top:4px}.entity-info-display{display:flex;flex-direction:column;gap:2px;padding:4px 8px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;background:var(--kendo-color-surface-alt, #f8f9fa)}.entity-info-main{display:flex;align-items:center;gap:4px}.entity-name{flex:1;font-size:.85rem;font-weight:600}.entity-info-details{display:flex;align-items:center;gap:2px;font-size:.7rem;font-family:monospace;color:var(--kendo-color-subtle, #6c757d)}.entity-detail-separator{color:var(--kendo-color-subtle, #6c757d)}.expression-feedback{font-size:.75rem;padding:2px 0;font-family:monospace}.expression-error{color:var(--kendo-color-error, #dc3545)}.expression-success{color:var(--kendo-color-success, #28a745)}\n"] }]
7305
8063
  }], propDecorators: { mappings: [{
7306
8064
  type: Input
7307
8065
  }], sourceDataPoints: [{
7308
8066
  type: Input
8067
+ }], expressionValidator: [{
8068
+ type: Input
7309
8069
  }], addMapping: [{
7310
8070
  type: Output
7311
8071
  }], removeMapping: [{
@@ -7318,6 +8078,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
7318
8078
  type: Output
7319
8079
  }], mappingChanged: [{
7320
8080
  type: Output
8081
+ }], navigateToTarget: [{
8082
+ type: Output
7321
8083
  }], saveAll: [{
7322
8084
  type: Output
7323
8085
  }] } });
@@ -7333,6 +8095,11 @@ class EntityDetailViewComponent {
7333
8095
  showDataMapping = true;
7334
8096
  dataMappings = [];
7335
8097
  sourceDataPoints = [];
8098
+ /**
8099
+ * Optional expression validator function passed through to DataMappingListComponent.
8100
+ * When provided, mapping expressions are validated on change with visual feedback.
8101
+ */
8102
+ expressionValidator;
7336
8103
  retry = new EventEmitter();
7337
8104
  propertyChange = new EventEmitter();
7338
8105
  navigateToEntity = new EventEmitter();
@@ -7526,6 +8293,14 @@ class EntityDetailViewComponent {
7526
8293
  getAssociationCount() {
7527
8294
  return this.entity?.associations?.definitions?.totalCount ?? 0;
7528
8295
  }
8296
+ onNavigateToMappingTarget(mapping) {
8297
+ if (mapping.targetRtId && mapping.targetCkTypeId) {
8298
+ this.navigateToEntity.emit({
8299
+ rtId: mapping.targetRtId,
8300
+ ckTypeId: mapping.targetCkTypeId,
8301
+ });
8302
+ }
8303
+ }
7529
8304
  copyToClipboard(value, label) {
7530
8305
  if (!value)
7531
8306
  return;
@@ -7593,7 +8368,7 @@ class EntityDetailViewComponent {
7593
8368
  }
7594
8369
  }
7595
8370
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: EntityDetailViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7596
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: EntityDetailViewComponent, isStandalone: true, selector: "mm-entity-detail-view", inputs: { entity: "entity", loading: "loading", error: "error", showHeader: "showHeader", messages: "messages", showDataMapping: "showDataMapping", dataMappings: "dataMappings", sourceDataPoints: "sourceDataPoints" }, outputs: { retry: "retry", propertyChange: "propertyChange", navigateToEntity: "navigateToEntity", addMappingRequested: "addMappingRequested", removeMappingRequested: "removeMappingRequested", selectMappingTarget: "selectMappingTarget", selectSourceAttributeRequested: "selectSourceAttributeRequested", selectTargetAttributeRequested: "selectTargetAttributeRequested", mappingChanged: "mappingChanged", saveAllMappingsRequested: "saveAllMappingsRequested" }, viewQueries: [{ propertyName: "associationsDataSource", first: true, predicate: ["associationsDir"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
8371
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: EntityDetailViewComponent, isStandalone: true, selector: "mm-entity-detail-view", inputs: { entity: "entity", loading: "loading", error: "error", showHeader: "showHeader", messages: "messages", showDataMapping: "showDataMapping", dataMappings: "dataMappings", sourceDataPoints: "sourceDataPoints", expressionValidator: "expressionValidator" }, outputs: { retry: "retry", propertyChange: "propertyChange", navigateToEntity: "navigateToEntity", addMappingRequested: "addMappingRequested", removeMappingRequested: "removeMappingRequested", selectMappingTarget: "selectMappingTarget", selectSourceAttributeRequested: "selectSourceAttributeRequested", selectTargetAttributeRequested: "selectTargetAttributeRequested", mappingChanged: "mappingChanged", saveAllMappingsRequested: "saveAllMappingsRequested" }, viewQueries: [{ propertyName: "associationsDataSource", first: true, predicate: ["associationsDir"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
7597
8372
  @if (loading) {
7598
8373
  <div class="loading-state">
7599
8374
  <p>{{ _messages.loadingEntityDetails }}</p>
@@ -7810,12 +8585,14 @@ class EntityDetailViewComponent {
7810
8585
  <mm-data-mapping-list
7811
8586
  [mappings]="dataMappings"
7812
8587
  [sourceDataPoints]="sourceDataPoints"
8588
+ [expressionValidator]="expressionValidator"
7813
8589
  (addMapping)="addMappingRequested.emit()"
7814
8590
  (removeMapping)="removeMappingRequested.emit($event)"
7815
8591
  (selectTarget)="selectMappingTarget.emit($event)"
7816
8592
  (selectSourceAttribute)="selectSourceAttributeRequested.emit($event)"
7817
8593
  (selectTargetAttribute)="selectTargetAttributeRequested.emit($event)"
7818
8594
  (mappingChanged)="mappingChanged.emit($event)"
8595
+ (navigateToTarget)="onNavigateToMappingTarget($event)"
7819
8596
  (saveAll)="saveAllMappingsRequested.emit()"
7820
8597
  ></mm-data-mapping-list>
7821
8598
  </div>
@@ -7825,7 +8602,7 @@ class EntityDetailViewComponent {
7825
8602
  </kendo-tabstrip>
7826
8603
  </div>
7827
8604
  }
7828
- `, isInline: true, styles: [".loading-state,.error-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px;text-align:center;min-height:200px}.loading-state .error-message,.error-state .error-message{margin-bottom:16px;font-family:Roboto,sans-serif;color:#e74c3c}.entity-content{flex:1;display:flex;flex-direction:column;gap:24px}.entity-content .basic-info-card{padding:20px 24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;position:relative;box-shadow:0 4px 20px #0006,0 0 15px var(--octo-mint-08)}.entity-content .basic-info-card:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50);border-radius:4px 0 0 4px}.entity-content .basic-info-card .basic-info-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:20px}.entity-content .basic-info-card .basic-info-grid .info-item{display:flex;flex-direction:column;gap:8px}.entity-content .basic-info-card .basic-info-grid .info-item label{font-family:Montserrat,sans-serif;font-weight:600;font-size:.75rem;letter-spacing:.5px;text-transform:uppercase;color:var(--octo-mint-80)}.entity-content .basic-info-card .basic-info-grid .info-item .value{font-family:Roboto Mono,monospace;font-size:.875rem;color:rgba(var(--octo-text-color),.9);word-break:break-word;background:var(--deep-sea-40);padding:8px 12px;border-radius:4px;border:1px solid var(--octo-mint-15)}.entity-content .basic-info-card .basic-info-grid .info-item.with-action .value-with-action{display:flex;align-items:center}.entity-content .basic-info-card .basic-info-grid .info-item.with-action .value-with-action .value{flex:1;font-family:Roboto Mono,monospace;font-size:.875rem;color:rgba(var(--octo-text-color),.9);word-break:break-word}.entity-content .entity-tabs{flex:1;min-height:400px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;overflow:hidden;position:relative}.entity-content .entity-tabs:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50);z-index:1}.entity-content .entity-tabs ::ng-deep .k-tabstrip{background:transparent}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper{background:linear-gradient(90deg,var(--octo-mint-10),transparent);border-bottom:1px solid var(--octo-mint-20);padding-left:20px}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper .k-tabstrip-items .k-item{background:transparent;border:none;color:rgba(var(--octo-text-color),.7);font-family:Montserrat,sans-serif;font-weight:600;font-size:.8rem;letter-spacing:.5px;text-transform:uppercase;padding:12px 20px;margin-right:4px;border-radius:4px 4px 0 0;transition:all .2s ease}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper .k-tabstrip-items .k-item:hover{background:var(--octo-mint-10);color:var(--octo-mint)}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper .k-tabstrip-items .k-item.k-active{background:linear-gradient(180deg,var(--octo-mint-20),transparent);color:var(--octo-mint);border-bottom:2px solid var(--octo-mint);box-shadow:0 0 10px var(--octo-mint-30)}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-content{background:transparent;border:none;padding:0}.entity-content .entity-tabs .tab-content{padding:20px 24px;height:100%;overflow-y:auto}.entity-content .entity-tabs .tab-content .empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 40px;text-align:center}.entity-content .entity-tabs .tab-content .empty-state kendo-svgicon{font-size:64px;margin-bottom:20px;color:var(--octo-mint-40);text-shadow:0 0 20px var(--octo-mint-30)}.entity-content .entity-tabs .tab-content .empty-state p{margin:0;font-family:Montserrat,sans-serif;font-size:.9rem;color:rgba(var(--octo-text-color),.5);letter-spacing:.5px}.entity-content .entity-tabs .tab-content.properties-tab{padding:0;height:100%}.entity-content .entity-tabs .tab-content.properties-tab mm-property-grid{display:block;height:100%;width:100%}.entity-content .entity-tabs .tab-content.associations-tab{display:flex;flex-direction:column;gap:16px;padding:0;height:100%}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:16px;padding:16px 20px;background:linear-gradient(90deg,var(--octo-mint-05),transparent);border-bottom:1px solid var(--octo-mint-20)}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group{display:flex;align-items:center;gap:10px}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group label{font-family:Montserrat,sans-serif;font-weight:600;font-size:.75rem;letter-spacing:.5px;text-transform:uppercase;color:var(--octo-mint-80);white-space:nowrap}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group kendo-dropdownlist{width:160px}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group kendo-textbox{width:220px}.entity-content .entity-tabs .tab-content.associations-tab mm-list-view{flex:1;display:block;height:calc(100% - 70px)}.entity-content .entity-tabs .tab-content.mapping-tab{padding:12px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-empty{display:flex;flex-direction:column;align-items:center;padding:24px 20px;text-align:center}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-empty kendo-svgicon{font-size:36px;margin-bottom:10px;opacity:.5}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-empty p{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-config{display:flex;flex-direction:column;gap:20px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-section{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:6px;overflow:hidden}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-section .section-header{padding:8px 14px;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:1px;color:var(--kendo-color-on-primary, #ffffff);background:var(--kendo-color-primary, #ff6358)}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-section .section-body{padding:12px 14px;display:flex;flex-direction:column;gap:10px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-field{display:flex;flex-direction:column;gap:4px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-field label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d)}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-target-display{display:flex;align-items:center;gap:8px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-target-display .target-type{font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);font-family:monospace;padding:2px 6px;border-radius:3px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6)}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-target-display .target-name{font-weight:600;flex:1}.entity-content .entity-tabs .tab-content.mapping-tab .field-hint{font-size:.7rem;color:var(--kendo-color-subtle, #6c757d);font-style:italic;line-height:1.3}.entity-content .entity-tabs .tab-content.mapping-tab .attribute-picker{display:flex;align-items:center;gap:8px;padding:4px 10px;border-radius:4px;border:1px solid var(--kendo-color-border, #dee2e6);background:var(--kendo-color-surface-alt, #f8f9fa);min-height:32px}.entity-content .entity-tabs .tab-content.mapping-tab .attribute-picker .attribute-value{flex:1;font-family:monospace;font-size:.85rem}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-actions{display:flex;gap:8px;justify-content:flex-end;padding-top:4px}@media(max-width:768px){.entity-content{gap:16px}.entity-content .basic-info-card{padding:16px 20px}.entity-content .basic-info-card .basic-info-grid{grid-template-columns:1fr;gap:16px}.entity-content .entity-tabs{min-height:300px}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper{padding-left:12px}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper .k-tabstrip-items .k-item{padding:10px 14px;font-size:.75rem}.entity-content .entity-tabs .tab-content{padding:16px}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar{flex-direction:column;align-items:flex-start;gap:12px}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group{width:100%}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group kendo-dropdownlist,.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group kendo-textbox{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TabStripModule }, { kind: "component", type: i1$4.TabStripComponent, selector: "kendo-tabstrip", inputs: ["height", "animate", "tabAlignment", "tabPosition", "keepTabContent", "closable", "scrollable", "size", "closeIcon", "closeIconClass", "closeSVGIcon", "showContentArea"], outputs: ["tabSelect", "tabClose", "tabScroll"], exportAs: ["kendoTabStrip"] }, { kind: "component", type: i1$4.TabStripTabComponent, selector: "kendo-tabstrip-tab", inputs: ["title", "disabled", "cssClass", "cssStyle", "selected", "closable", "closeIcon", "closeIconClass", "closeSVGIcon"], exportAs: ["kendoTabStripTab"] }, { kind: "directive", type: i1$4.TabContentDirective, selector: "[kendoTabContent]" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i5$1.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i1$4.CardComponent, selector: "kendo-card", inputs: ["orientation", "width"] }, { kind: "component", type: i1$4.CardBodyComponent, selector: "kendo-card-body" }, { kind: "component", type: i1$4.CardHeaderComponent, selector: "kendo-card-header" }, { kind: "ngmodule", type: DropDownListModule }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "ngmodule", type: TextBoxModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "component", type: PropertyGridComponent, selector: "mm-property-grid", inputs: ["data", "config", "showTypeColumn"], outputs: ["propertyChange", "saveRequested", "binaryDownload"] }, { kind: "component", type: ListViewComponent, selector: "mm-list-view", inputs: ["pageSize", "skip", "rowIsClickable", "showRowCheckBoxes", "showRowSelectAllCheckBox", "contextMenuType", "leftToolbarActions", "rightToolbarActions", "actionCommandItems", "contextMenuCommandItems", "excelExportFileName", "pdfExportFileName", "pageable", "sortable", "rowFilterEnabled", "searchTextBoxEnabled", "rowClass", "messages", "selectable", "columns"], outputs: ["rowClicked"] }, { kind: "directive", type: EntityAssociationsDataSourceDirective, selector: "[mmEntityAssociationsDataSource]", exportAs: ["mmEntityAssociationsDataSource"] }, { kind: "component", type: DataMappingListComponent, selector: "mm-data-mapping-list", inputs: ["mappings", "sourceDataPoints"], outputs: ["addMapping", "removeMapping", "selectTarget", "selectSourceAttribute", "selectTargetAttribute", "mappingChanged", "saveAll"] }] });
8605
+ `, isInline: true, styles: [".loading-state,.error-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px;text-align:center;min-height:200px}.loading-state .error-message,.error-state .error-message{margin-bottom:16px;font-family:Roboto,sans-serif;color:#e74c3c}.entity-content{flex:1;display:flex;flex-direction:column;gap:24px}.entity-content .basic-info-card{padding:20px 24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;position:relative;box-shadow:0 4px 20px #0006,0 0 15px var(--octo-mint-08)}.entity-content .basic-info-card:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50);border-radius:4px 0 0 4px}.entity-content .basic-info-card .basic-info-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:20px}.entity-content .basic-info-card .basic-info-grid .info-item{display:flex;flex-direction:column;gap:8px}.entity-content .basic-info-card .basic-info-grid .info-item label{font-family:Montserrat,sans-serif;font-weight:600;font-size:.75rem;letter-spacing:.5px;text-transform:uppercase;color:var(--octo-mint-80)}.entity-content .basic-info-card .basic-info-grid .info-item .value{font-family:Roboto Mono,monospace;font-size:.875rem;color:rgba(var(--octo-text-color),.9);word-break:break-word;background:var(--deep-sea-40);padding:8px 12px;border-radius:4px;border:1px solid var(--octo-mint-15)}.entity-content .basic-info-card .basic-info-grid .info-item.with-action .value-with-action{display:flex;align-items:center}.entity-content .basic-info-card .basic-info-grid .info-item.with-action .value-with-action .value{flex:1;font-family:Roboto Mono,monospace;font-size:.875rem;color:rgba(var(--octo-text-color),.9);word-break:break-word}.entity-content .entity-tabs{flex:1;min-height:400px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;overflow:hidden;position:relative}.entity-content .entity-tabs:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50);z-index:1}.entity-content .entity-tabs ::ng-deep .k-tabstrip{background:transparent}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper{background:linear-gradient(90deg,var(--octo-mint-10),transparent);border-bottom:1px solid var(--octo-mint-20);padding-left:20px}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper .k-tabstrip-items .k-item{background:transparent;border:none;color:rgba(var(--octo-text-color),.7);font-family:Montserrat,sans-serif;font-weight:600;font-size:.8rem;letter-spacing:.5px;text-transform:uppercase;padding:12px 20px;margin-right:4px;border-radius:4px 4px 0 0;transition:all .2s ease}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper .k-tabstrip-items .k-item:hover{background:var(--octo-mint-10);color:var(--octo-mint)}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper .k-tabstrip-items .k-item.k-active{background:linear-gradient(180deg,var(--octo-mint-20),transparent);color:var(--octo-mint);border-bottom:2px solid var(--octo-mint);box-shadow:0 0 10px var(--octo-mint-30)}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-content{background:transparent;border:none;padding:0}.entity-content .entity-tabs .tab-content{padding:20px 24px;height:100%;overflow-y:auto}.entity-content .entity-tabs .tab-content .empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 40px;text-align:center}.entity-content .entity-tabs .tab-content .empty-state kendo-svgicon{font-size:64px;margin-bottom:20px;color:var(--octo-mint-40);text-shadow:0 0 20px var(--octo-mint-30)}.entity-content .entity-tabs .tab-content .empty-state p{margin:0;font-family:Montserrat,sans-serif;font-size:.9rem;color:rgba(var(--octo-text-color),.5);letter-spacing:.5px}.entity-content .entity-tabs .tab-content.properties-tab{padding:0;height:100%}.entity-content .entity-tabs .tab-content.properties-tab mm-property-grid{display:block;height:100%;width:100%}.entity-content .entity-tabs .tab-content.associations-tab{display:flex;flex-direction:column;gap:16px;padding:0;height:100%}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:16px;padding:16px 20px;background:linear-gradient(90deg,var(--octo-mint-05),transparent);border-bottom:1px solid var(--octo-mint-20)}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group{display:flex;align-items:center;gap:10px}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group label{font-family:Montserrat,sans-serif;font-weight:600;font-size:.75rem;letter-spacing:.5px;text-transform:uppercase;color:var(--octo-mint-80);white-space:nowrap}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group kendo-dropdownlist{width:160px}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group kendo-textbox{width:220px}.entity-content .entity-tabs .tab-content.associations-tab mm-list-view{flex:1;display:block;height:calc(100% - 70px)}.entity-content .entity-tabs .tab-content.mapping-tab{padding:12px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-empty{display:flex;flex-direction:column;align-items:center;padding:24px 20px;text-align:center}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-empty kendo-svgicon{font-size:36px;margin-bottom:10px;opacity:.5}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-empty p{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-config{display:flex;flex-direction:column;gap:20px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-section{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:6px;overflow:hidden}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-section .section-header{padding:8px 14px;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:1px;color:var(--kendo-color-on-primary, #ffffff);background:var(--kendo-color-primary, #ff6358)}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-section .section-body{padding:12px 14px;display:flex;flex-direction:column;gap:10px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-field{display:flex;flex-direction:column;gap:4px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-field label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d)}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-target-display{display:flex;align-items:center;gap:8px}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-target-display .target-type{font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);font-family:monospace;padding:2px 6px;border-radius:3px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6)}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-target-display .target-name{font-weight:600;flex:1}.entity-content .entity-tabs .tab-content.mapping-tab .field-hint{font-size:.7rem;color:var(--kendo-color-subtle, #6c757d);font-style:italic;line-height:1.3}.entity-content .entity-tabs .tab-content.mapping-tab .attribute-picker{display:flex;align-items:center;gap:8px;padding:4px 10px;border-radius:4px;border:1px solid var(--kendo-color-border, #dee2e6);background:var(--kendo-color-surface-alt, #f8f9fa);min-height:32px}.entity-content .entity-tabs .tab-content.mapping-tab .attribute-picker .attribute-value{flex:1;font-family:monospace;font-size:.85rem}.entity-content .entity-tabs .tab-content.mapping-tab .mapping-actions{display:flex;gap:8px;justify-content:flex-end;padding-top:4px}@media(max-width:768px){.entity-content{gap:16px}.entity-content .basic-info-card{padding:16px 20px}.entity-content .basic-info-card .basic-info-grid{grid-template-columns:1fr;gap:16px}.entity-content .entity-tabs{min-height:300px}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper{padding-left:12px}.entity-content .entity-tabs ::ng-deep .k-tabstrip .k-tabstrip-items-wrapper .k-tabstrip-items .k-item{padding:10px 14px;font-size:.75rem}.entity-content .entity-tabs .tab-content{padding:16px}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar{flex-direction:column;align-items:flex-start;gap:12px}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group{width:100%}.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group kendo-dropdownlist,.entity-content .entity-tabs .tab-content.associations-tab .associations-toolbar .filter-group kendo-textbox{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TabStripModule }, { kind: "component", type: i1$4.TabStripComponent, selector: "kendo-tabstrip", inputs: ["height", "animate", "tabAlignment", "tabPosition", "keepTabContent", "closable", "scrollable", "size", "closeIcon", "closeIconClass", "closeSVGIcon", "showContentArea"], outputs: ["tabSelect", "tabClose", "tabScroll"], exportAs: ["kendoTabStrip"] }, { kind: "component", type: i1$4.TabStripTabComponent, selector: "kendo-tabstrip-tab", inputs: ["title", "disabled", "cssClass", "cssStyle", "selected", "closable", "closeIcon", "closeIconClass", "closeSVGIcon"], exportAs: ["kendoTabStripTab"] }, { kind: "directive", type: i1$4.TabContentDirective, selector: "[kendoTabContent]" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i5$1.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i1$4.CardComponent, selector: "kendo-card", inputs: ["orientation", "width"] }, { kind: "component", type: i1$4.CardBodyComponent, selector: "kendo-card-body" }, { kind: "component", type: i1$4.CardHeaderComponent, selector: "kendo-card-header" }, { kind: "ngmodule", type: DropDownListModule }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "ngmodule", type: TextBoxModule }, { kind: "component", type: i5.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "component", type: PropertyGridComponent, selector: "mm-property-grid", inputs: ["data", "config", "showTypeColumn"], outputs: ["propertyChange", "saveRequested", "binaryDownload"] }, { kind: "component", type: ListViewComponent, selector: "mm-list-view", inputs: ["pageSize", "skip", "rowIsClickable", "showRowCheckBoxes", "showRowSelectAllCheckBox", "contextMenuType", "leftToolbarActions", "rightToolbarActions", "actionCommandItems", "contextMenuCommandItems", "excelExportFileName", "pdfExportFileName", "pageable", "sortable", "rowFilterEnabled", "searchTextBoxEnabled", "rowClass", "messages", "selectable", "columns"], outputs: ["rowClicked"] }, { kind: "directive", type: EntityAssociationsDataSourceDirective, selector: "[mmEntityAssociationsDataSource]", exportAs: ["mmEntityAssociationsDataSource"] }, { kind: "component", type: DataMappingListComponent, selector: "mm-data-mapping-list", inputs: ["mappings", "sourceDataPoints", "expressionValidator"], outputs: ["addMapping", "removeMapping", "selectTarget", "selectSourceAttribute", "selectTargetAttribute", "mappingChanged", "navigateToTarget", "saveAll"] }] });
7829
8606
  }
7830
8607
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: EntityDetailViewComponent, decorators: [{
7831
8608
  type: Component,
@@ -8058,12 +8835,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
8058
8835
  <mm-data-mapping-list
8059
8836
  [mappings]="dataMappings"
8060
8837
  [sourceDataPoints]="sourceDataPoints"
8838
+ [expressionValidator]="expressionValidator"
8061
8839
  (addMapping)="addMappingRequested.emit()"
8062
8840
  (removeMapping)="removeMappingRequested.emit($event)"
8063
8841
  (selectTarget)="selectMappingTarget.emit($event)"
8064
8842
  (selectSourceAttribute)="selectSourceAttributeRequested.emit($event)"
8065
8843
  (selectTargetAttribute)="selectTargetAttributeRequested.emit($event)"
8066
8844
  (mappingChanged)="mappingChanged.emit($event)"
8845
+ (navigateToTarget)="onNavigateToMappingTarget($event)"
8067
8846
  (saveAll)="saveAllMappingsRequested.emit()"
8068
8847
  ></mm-data-mapping-list>
8069
8848
  </div>
@@ -8090,6 +8869,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
8090
8869
  type: Input
8091
8870
  }], sourceDataPoints: [{
8092
8871
  type: Input
8872
+ }], expressionValidator: [{
8873
+ type: Input
8093
8874
  }], retry: [{
8094
8875
  type: Output
8095
8876
  }], propertyChange: [{
@@ -8609,7 +9390,7 @@ class EntityDetailComponent {
8609
9390
  >
8610
9391
  </mm-entity-detail-view>
8611
9392
  </div>
8612
- `, isInline: true, styles: [":host{display:block;height:100%;width:100%}.entity-detail{height:100%;display:flex;flex-direction:column;padding:24px;box-sizing:border-box;overflow-y:auto}.entity-detail .entity-detail-header{display:flex;align-items:center;gap:20px;margin-bottom:24px;padding:20px 24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;position:relative;box-shadow:0 4px 20px #0006,0 0 15px var(--octo-mint-08)}.entity-detail .entity-detail-header:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50);border-radius:4px 0 0 4px}.entity-detail .entity-detail-header .header-info{flex:1}.entity-detail .entity-detail-header .header-info .entity-title h2{margin:0 0 8px;font-family:Montserrat,sans-serif;font-size:1.4rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-text-color);text-shadow:0 0 10px rgba(0,0,0,.3)}.entity-detail .entity-detail-header .header-info .entity-type{margin:0;font-family:Roboto Mono,monospace;font-size:.8rem;background:var(--deep-sea-60);padding:6px 12px;border-radius:4px;border:1px solid var(--neo-cyan-30);display:inline-block;color:var(--neo-cyan)}.entity-detail mm-entity-detail-view{flex:1;display:flex;flex-direction:column;overflow-y:auto}@media(max-width:768px){.entity-detail{padding:16px}.entity-detail .entity-detail-header{flex-direction:column;align-items:flex-start;gap:12px;padding:16px 20px}.entity-detail .entity-detail-header .header-info .entity-title h2{font-size:1.1rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: EntityDetailViewComponent, selector: "mm-entity-detail-view", inputs: ["entity", "loading", "error", "showHeader", "messages", "showDataMapping", "dataMappings", "sourceDataPoints"], outputs: ["retry", "propertyChange", "navigateToEntity", "addMappingRequested", "removeMappingRequested", "selectMappingTarget", "selectSourceAttributeRequested", "selectTargetAttributeRequested", "mappingChanged", "saveAllMappingsRequested"] }] });
9393
+ `, isInline: true, styles: [":host{display:block;height:100%;width:100%}.entity-detail{height:100%;display:flex;flex-direction:column;padding:24px;box-sizing:border-box;overflow-y:auto}.entity-detail .entity-detail-header{display:flex;align-items:center;gap:20px;margin-bottom:24px;padding:20px 24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;position:relative;box-shadow:0 4px 20px #0006,0 0 15px var(--octo-mint-08)}.entity-detail .entity-detail-header:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50);border-radius:4px 0 0 4px}.entity-detail .entity-detail-header .header-info{flex:1}.entity-detail .entity-detail-header .header-info .entity-title h2{margin:0 0 8px;font-family:Montserrat,sans-serif;font-size:1.4rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-text-color);text-shadow:0 0 10px rgba(0,0,0,.3)}.entity-detail .entity-detail-header .header-info .entity-type{margin:0;font-family:Roboto Mono,monospace;font-size:.8rem;background:var(--deep-sea-60);padding:6px 12px;border-radius:4px;border:1px solid var(--neo-cyan-30);display:inline-block;color:var(--neo-cyan)}.entity-detail mm-entity-detail-view{flex:1;display:flex;flex-direction:column;overflow-y:auto}@media(max-width:768px){.entity-detail{padding:16px}.entity-detail .entity-detail-header{flex-direction:column;align-items:flex-start;gap:12px;padding:16px 20px}.entity-detail .entity-detail-header .header-info .entity-title h2{font-size:1.1rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: EntityDetailViewComponent, selector: "mm-entity-detail-view", inputs: ["entity", "loading", "error", "showHeader", "messages", "showDataMapping", "dataMappings", "sourceDataPoints", "expressionValidator"], outputs: ["retry", "propertyChange", "navigateToEntity", "addMappingRequested", "removeMappingRequested", "selectMappingTarget", "selectSourceAttributeRequested", "selectTargetAttributeRequested", "mappingChanged", "saveAllMappingsRequested"] }] });
8613
9394
  }
8614
9395
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: EntityDetailComponent, decorators: [{
8615
9396
  type: Component,
@@ -11975,6 +12756,10 @@ class RuntimeBrowserDetailsComponent {
11975
12756
  // Data Mapping state (list of DataPointMapping entities)
11976
12757
  dataMappings = [];
11977
12758
  sourceDataPoints = [];
12759
+ /**
12760
+ * Optional expression validator function passed through to EntityDetailViewComponent → DataMappingListComponent.
12761
+ */
12762
+ expressionValidator;
11978
12763
  detailsIcon = eyeIcon;
11979
12764
  fullEntity = null;
11980
12765
  loadRequestToken = 0;
@@ -12654,7 +13439,7 @@ class RuntimeBrowserDetailsComponent {
12654
13439
  }
12655
13440
  }
12656
13441
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RuntimeBrowserDetailsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12657
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: RuntimeBrowserDetailsComponent, isStandalone: true, selector: "mm-runtime-browser-details", inputs: { selectedItem: "selectedItem", showDataMapping: "showDataMapping", messages: "messages" }, outputs: { entitySaved: "entitySaved" }, viewQueries: [{ propertyName: "dataSourceDirective", first: true, predicate: ["dir"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
13442
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: RuntimeBrowserDetailsComponent, isStandalone: true, selector: "mm-runtime-browser-details", inputs: { selectedItem: "selectedItem", showDataMapping: "showDataMapping", messages: "messages", expressionValidator: "expressionValidator" }, outputs: { entitySaved: "entitySaved" }, viewQueries: [{ propertyName: "dataSourceDirective", first: true, predicate: ["dir"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
12658
13443
  <div class="runtime-browser-details">
12659
13444
  @if (isCreateModeEnabled) {
12660
13445
  <mm-create-editor-component
@@ -12696,6 +13481,7 @@ class RuntimeBrowserDetailsComponent {
12696
13481
  [showDataMapping]="showDataMapping"
12697
13482
  [dataMappings]="dataMappings"
12698
13483
  [sourceDataPoints]="sourceDataPoints"
13484
+ [expressionValidator]="expressionValidator"
12699
13485
  (retry)="loadFullEntityDetails()"
12700
13486
  (navigateToEntity)="
12701
13487
  navigateToEntity($event.rtId, $event.ckTypeId)
@@ -12794,7 +13580,7 @@ class RuntimeBrowserDetailsComponent {
12794
13580
  }
12795
13581
  }
12796
13582
  </div>
12797
- `, isInline: true, styles: [":host{display:block;height:100%;width:100%}.runtime-browser-details{display:flex;flex-direction:column;flex:1;min-height:0;padding:24px;box-sizing:border-box;overflow-y:auto;background:linear-gradient(180deg,var(--surface-elevated) 0%,var(--deep-sea) 100%)}.runtime-browser-details .no-selection{display:flex;align-items:center;justify-content:center;flex:1;min-height:0}.runtime-browser-details .no-selection .placeholder-content{text-align:center;max-width:400px;padding:40px;background:linear-gradient(135deg,var(--iron-navy-80),var(--surface-elevated-90));border:1px solid var(--octo-mint-30);border-radius:8px 24px;box-shadow:0 0 30px var(--octo-mint-10),inset 0 0 40px var(--octo-mint-03)}.runtime-browser-details .no-selection .placeholder-content .placeholder-icon{margin-bottom:24px}.runtime-browser-details .no-selection .placeholder-content .placeholder-icon .k-icon{font-size:64px;color:var(--octo-mint-50);text-shadow:0 0 20px var(--octo-mint-50);animation:lcars-icon-pulse 3s ease-in-out infinite}.runtime-browser-details .no-selection .placeholder-content h3{margin:0 0 12px;font-family:Montserrat,sans-serif;font-size:1.4rem;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:var(--octo-mint);text-shadow:0 0 15px var(--octo-mint-50)}.runtime-browser-details .no-selection .placeholder-content p{margin:0;font-family:Montserrat,sans-serif;font-size:.9rem;line-height:1.6;color:rgba(var(--octo-text-color),.7);letter-spacing:.5px}.runtime-browser-details .entity-details{height:100%;display:flex;flex-direction:column;overflow:hidden}.runtime-browser-details .entity-details .details-header{display:flex;align-items:center;gap:16px;padding:16px 20px 16px 24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;margin-bottom:20px;position:relative;box-shadow:0 4px 20px #0006,0 0 15px var(--octo-mint-08)}.runtime-browser-details .entity-details .details-header:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50);border-radius:4px 0 0 4px}.runtime-browser-details .entity-details .details-header .entity-icon{font-size:28px;color:var(--octo-mint);text-shadow:0 0 10px var(--octo-mint-50)}.runtime-browser-details .entity-details .details-header .entity-title{flex:1}.runtime-browser-details .entity-details .details-header .entity-title h3{margin:0 0 6px;font-family:Montserrat,sans-serif;font-size:1.1rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-text-color)}.runtime-browser-details .entity-details .details-header .entity-title .entity-type{margin:0;font-family:Roboto Mono,monospace;font-size:.8rem;color:var(--neo-cyan);background:var(--deep-sea-60);padding:4px 10px;border-radius:4px;border:1px solid var(--neo-cyan-30);display:inline-block}.runtime-browser-details .entity-details mm-entity-detail-view{flex:1;display:flex;flex-direction:column;overflow-y:auto}.runtime-browser-details .entity-details .ck-model-details,.runtime-browser-details .entity-details .ck-models-root{padding:24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;position:relative}.runtime-browser-details .entity-details .ck-model-details:before,.runtime-browser-details .entity-details .ck-models-root:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50)}.runtime-browser-details .entity-details .ck-model-details h3,.runtime-browser-details .entity-details .ck-models-root h3{margin:0 0 20px;font-family:Montserrat,sans-serif;font-size:1.1rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-text-color)}.runtime-browser-details .entity-details .ck-model-details p,.runtime-browser-details .entity-details .ck-models-root p{margin:0 0 12px;font-family:Roboto,sans-serif;font-size:.9rem;color:rgba(var(--octo-text-color),.85)}.runtime-browser-details .entity-details .ck-model-details p strong,.runtime-browser-details .entity-details .ck-models-root p strong{font-family:Montserrat,sans-serif;font-weight:600;font-size:.8rem;letter-spacing:.5px;text-transform:uppercase;color:var(--octo-mint-80);margin-right:12px}.runtime-browser-details .entity-details .ck-model-details p.info-text,.runtime-browser-details .entity-details .ck-models-root p.info-text{color:rgba(var(--octo-text-color),.6);font-style:italic;margin-top:24px;padding:16px 20px;background:linear-gradient(135deg,var(--neo-cyan-10),var(--neo-cyan-05));border:1px solid var(--neo-cyan-30);border-radius:4px 12px}.runtime-browser-details .entity-details .ck-type-details{display:flex;flex-direction:column;height:100%;padding:0}.runtime-browser-details .entity-details .ck-type-details .type-header{margin-bottom:20px;padding:20px 24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;position:relative}.runtime-browser-details .entity-details .ck-type-details .type-header:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50)}.runtime-browser-details .entity-details .ck-type-details .type-header h3{margin:0 0 12px;font-family:Montserrat,sans-serif;font-size:1.1rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-text-color)}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata{display:flex;gap:10px;align-items:center}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata .badge{padding:6px 14px;border-radius:4px;font-family:Montserrat,sans-serif;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata .badge.abstract{background:linear-gradient(135deg,var(--royal-violet-30),var(--royal-violet-15));border:1px solid var(--royal-violet-50);color:var(--royal-violet-light-20);box-shadow:0 0 10px var(--royal-violet-30)}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata .badge.final{background:linear-gradient(135deg,var(--toffee-30),var(--toffee-15));border:1px solid var(--toffee-50);color:var(--toffee);box-shadow:0 0 10px var(--toffee-30)}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata .base-type{font-family:Roboto Mono,monospace;font-size:.8rem;color:var(--neo-cyan);background:var(--deep-sea-60);padding:4px 10px;border-radius:4px;border:1px solid var(--neo-cyan-30)}.runtime-browser-details .entity-details .ck-type-details .entities-table{flex:1;display:flex;flex-direction:column;overflow:hidden;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;padding:20px}.runtime-browser-details .entity-details .ck-type-details .entities-table h4{margin:0 0 16px;font-family:Montserrat,sans-serif;font-size:1rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-mint)}.runtime-browser-details .entity-details .ck-type-details .entities-table mm-list-view{flex:1;display:flex;flex-direction:column;overflow:hidden}@keyframes lcars-icon-pulse{0%,to{opacity:.5;transform:scale(1)}50%{opacity:.8;transform:scale(1.05)}}@media(max-width:768px){.runtime-browser-details{padding:16px}.runtime-browser-details .no-selection .placeholder-content{padding:24px}.runtime-browser-details .no-selection .placeholder-content .placeholder-icon .k-icon{font-size:48px}.runtime-browser-details .no-selection .placeholder-content h3{font-size:1.1rem}.runtime-browser-details .entity-details .details-header{flex-direction:column;align-items:flex-start;gap:12px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: EntityDetailViewComponent, selector: "mm-entity-detail-view", inputs: ["entity", "loading", "error", "showHeader", "messages", "showDataMapping", "dataMappings", "sourceDataPoints"], outputs: ["retry", "propertyChange", "navigateToEntity", "addMappingRequested", "removeMappingRequested", "selectMappingTarget", "selectSourceAttributeRequested", "selectTargetAttributeRequested", "mappingChanged", "saveAllMappingsRequested"] }, { kind: "component", type: ListViewComponent, selector: "mm-list-view", inputs: ["pageSize", "skip", "rowIsClickable", "showRowCheckBoxes", "showRowSelectAllCheckBox", "contextMenuType", "leftToolbarActions", "rightToolbarActions", "actionCommandItems", "contextMenuCommandItems", "excelExportFileName", "pdfExportFileName", "pageable", "sortable", "rowFilterEnabled", "searchTextBoxEnabled", "rowClass", "messages", "selectable", "columns"], outputs: ["rowClicked"] }, { kind: "directive", type: CkTypeEntitiesDataSourceDirective, selector: "[mmCkTypeEntitiesDataSource]", exportAs: ["mmCkTypeEntitiesDataSource"] }, { kind: "component", type: CreateEditorComponent, selector: "mm-create-editor-component", inputs: ["createInput", "messages"], outputs: ["createOutput", "cancelRequested"] }, { kind: "component", type: UpdateEditorComponent, selector: "mm-update-editor-component", inputs: ["updateInput", "messages"], outputs: ["updateOutput", "cancelRequested"] }] });
13583
+ `, isInline: true, styles: [":host{display:block;height:100%;width:100%}.runtime-browser-details{display:flex;flex-direction:column;flex:1;min-height:0;padding:24px;box-sizing:border-box;overflow-y:auto;background:linear-gradient(180deg,var(--surface-elevated) 0%,var(--deep-sea) 100%)}.runtime-browser-details .no-selection{display:flex;align-items:center;justify-content:center;flex:1;min-height:0}.runtime-browser-details .no-selection .placeholder-content{text-align:center;max-width:400px;padding:40px;background:linear-gradient(135deg,var(--iron-navy-80),var(--surface-elevated-90));border:1px solid var(--octo-mint-30);border-radius:8px 24px;box-shadow:0 0 30px var(--octo-mint-10),inset 0 0 40px var(--octo-mint-03)}.runtime-browser-details .no-selection .placeholder-content .placeholder-icon{margin-bottom:24px}.runtime-browser-details .no-selection .placeholder-content .placeholder-icon .k-icon{font-size:64px;color:var(--octo-mint-50);text-shadow:0 0 20px var(--octo-mint-50);animation:lcars-icon-pulse 3s ease-in-out infinite}.runtime-browser-details .no-selection .placeholder-content h3{margin:0 0 12px;font-family:Montserrat,sans-serif;font-size:1.4rem;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:var(--octo-mint);text-shadow:0 0 15px var(--octo-mint-50)}.runtime-browser-details .no-selection .placeholder-content p{margin:0;font-family:Montserrat,sans-serif;font-size:.9rem;line-height:1.6;color:rgba(var(--octo-text-color),.7);letter-spacing:.5px}.runtime-browser-details .entity-details{height:100%;display:flex;flex-direction:column;overflow:hidden}.runtime-browser-details .entity-details .details-header{display:flex;align-items:center;gap:16px;padding:16px 20px 16px 24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;margin-bottom:20px;position:relative;box-shadow:0 4px 20px #0006,0 0 15px var(--octo-mint-08)}.runtime-browser-details .entity-details .details-header:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50);border-radius:4px 0 0 4px}.runtime-browser-details .entity-details .details-header .entity-icon{font-size:28px;color:var(--octo-mint);text-shadow:0 0 10px var(--octo-mint-50)}.runtime-browser-details .entity-details .details-header .entity-title{flex:1}.runtime-browser-details .entity-details .details-header .entity-title h3{margin:0 0 6px;font-family:Montserrat,sans-serif;font-size:1.1rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-text-color)}.runtime-browser-details .entity-details .details-header .entity-title .entity-type{margin:0;font-family:Roboto Mono,monospace;font-size:.8rem;color:var(--neo-cyan);background:var(--deep-sea-60);padding:4px 10px;border-radius:4px;border:1px solid var(--neo-cyan-30);display:inline-block}.runtime-browser-details .entity-details mm-entity-detail-view{flex:1;display:flex;flex-direction:column;overflow-y:auto}.runtime-browser-details .entity-details .ck-model-details,.runtime-browser-details .entity-details .ck-models-root{padding:24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;position:relative}.runtime-browser-details .entity-details .ck-model-details:before,.runtime-browser-details .entity-details .ck-models-root:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50)}.runtime-browser-details .entity-details .ck-model-details h3,.runtime-browser-details .entity-details .ck-models-root h3{margin:0 0 20px;font-family:Montserrat,sans-serif;font-size:1.1rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-text-color)}.runtime-browser-details .entity-details .ck-model-details p,.runtime-browser-details .entity-details .ck-models-root p{margin:0 0 12px;font-family:Roboto,sans-serif;font-size:.9rem;color:rgba(var(--octo-text-color),.85)}.runtime-browser-details .entity-details .ck-model-details p strong,.runtime-browser-details .entity-details .ck-models-root p strong{font-family:Montserrat,sans-serif;font-weight:600;font-size:.8rem;letter-spacing:.5px;text-transform:uppercase;color:var(--octo-mint-80);margin-right:12px}.runtime-browser-details .entity-details .ck-model-details p.info-text,.runtime-browser-details .entity-details .ck-models-root p.info-text{color:rgba(var(--octo-text-color),.6);font-style:italic;margin-top:24px;padding:16px 20px;background:linear-gradient(135deg,var(--neo-cyan-10),var(--neo-cyan-05));border:1px solid var(--neo-cyan-30);border-radius:4px 12px}.runtime-browser-details .entity-details .ck-type-details{display:flex;flex-direction:column;height:100%;padding:0}.runtime-browser-details .entity-details .ck-type-details .type-header{margin-bottom:20px;padding:20px 24px;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;position:relative}.runtime-browser-details .entity-details .ck-type-details .type-header:before{content:\"\";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,var(--octo-mint),var(--neo-cyan),var(--royal-violet));box-shadow:0 0 10px var(--octo-mint-50)}.runtime-browser-details .entity-details .ck-type-details .type-header h3{margin:0 0 12px;font-family:Montserrat,sans-serif;font-size:1.1rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-text-color)}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata{display:flex;gap:10px;align-items:center}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata .badge{padding:6px 14px;border-radius:4px;font-family:Montserrat,sans-serif;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata .badge.abstract{background:linear-gradient(135deg,var(--royal-violet-30),var(--royal-violet-15));border:1px solid var(--royal-violet-50);color:var(--royal-violet-light-20);box-shadow:0 0 10px var(--royal-violet-30)}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata .badge.final{background:linear-gradient(135deg,var(--toffee-30),var(--toffee-15));border:1px solid var(--toffee-50);color:var(--toffee);box-shadow:0 0 10px var(--toffee-30)}.runtime-browser-details .entity-details .ck-type-details .type-header .type-metadata .base-type{font-family:Roboto Mono,monospace;font-size:.8rem;color:var(--neo-cyan);background:var(--deep-sea-60);padding:4px 10px;border-radius:4px;border:1px solid var(--neo-cyan-30)}.runtime-browser-details .entity-details .ck-type-details .entities-table{flex:1;display:flex;flex-direction:column;overflow:hidden;background:linear-gradient(180deg,var(--iron-navy),var(--surface-elevated));border:1px solid var(--octo-mint-30);border-radius:4px 16px;padding:20px}.runtime-browser-details .entity-details .ck-type-details .entities-table h4{margin:0 0 16px;font-family:Montserrat,sans-serif;font-size:1rem;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--octo-mint)}.runtime-browser-details .entity-details .ck-type-details .entities-table mm-list-view{flex:1;display:flex;flex-direction:column;overflow:hidden}@keyframes lcars-icon-pulse{0%,to{opacity:.5;transform:scale(1)}50%{opacity:.8;transform:scale(1.05)}}@media(max-width:768px){.runtime-browser-details{padding:16px}.runtime-browser-details .no-selection .placeholder-content{padding:24px}.runtime-browser-details .no-selection .placeholder-content .placeholder-icon .k-icon{font-size:48px}.runtime-browser-details .no-selection .placeholder-content h3{font-size:1.1rem}.runtime-browser-details .entity-details .details-header{flex-direction:column;align-items:flex-start;gap:12px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: EntityDetailViewComponent, selector: "mm-entity-detail-view", inputs: ["entity", "loading", "error", "showHeader", "messages", "showDataMapping", "dataMappings", "sourceDataPoints", "expressionValidator"], outputs: ["retry", "propertyChange", "navigateToEntity", "addMappingRequested", "removeMappingRequested", "selectMappingTarget", "selectSourceAttributeRequested", "selectTargetAttributeRequested", "mappingChanged", "saveAllMappingsRequested"] }, { kind: "component", type: ListViewComponent, selector: "mm-list-view", inputs: ["pageSize", "skip", "rowIsClickable", "showRowCheckBoxes", "showRowSelectAllCheckBox", "contextMenuType", "leftToolbarActions", "rightToolbarActions", "actionCommandItems", "contextMenuCommandItems", "excelExportFileName", "pdfExportFileName", "pageable", "sortable", "rowFilterEnabled", "searchTextBoxEnabled", "rowClass", "messages", "selectable", "columns"], outputs: ["rowClicked"] }, { kind: "directive", type: CkTypeEntitiesDataSourceDirective, selector: "[mmCkTypeEntitiesDataSource]", exportAs: ["mmCkTypeEntitiesDataSource"] }, { kind: "component", type: CreateEditorComponent, selector: "mm-create-editor-component", inputs: ["createInput", "messages"], outputs: ["createOutput", "cancelRequested"] }, { kind: "component", type: UpdateEditorComponent, selector: "mm-update-editor-component", inputs: ["updateInput", "messages"], outputs: ["updateOutput", "cancelRequested"] }] });
12798
13584
  }
12799
13585
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RuntimeBrowserDetailsComponent, decorators: [{
12800
13586
  type: Component,
@@ -12849,6 +13635,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
12849
13635
  [showDataMapping]="showDataMapping"
12850
13636
  [dataMappings]="dataMappings"
12851
13637
  [sourceDataPoints]="sourceDataPoints"
13638
+ [expressionValidator]="expressionValidator"
12852
13639
  (retry)="loadFullEntityDetails()"
12853
13640
  (navigateToEntity)="
12854
13641
  navigateToEntity($event.rtId, $event.ckTypeId)
@@ -12959,6 +13746,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
12959
13746
  }], dataSourceDirective: [{
12960
13747
  type: ViewChild,
12961
13748
  args: ["dir", { static: false }]
13749
+ }], expressionValidator: [{
13750
+ type: Input
12962
13751
  }] } });
12963
13752
 
12964
13753
  class RuntimeBrowserComponent {
@@ -12977,6 +13766,11 @@ class RuntimeBrowserComponent {
12977
13766
  isEditing = false;
12978
13767
  messages = input({}, ...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
12979
13768
  showDataMapping = input(true, ...(ngDevMode ? [{ debugName: "showDataMapping" }] : /* istanbul ignore next */ []));
13769
+ /**
13770
+ * Optional expression validator function for DataPointMapping expressions.
13771
+ * Passed through to RuntimeBrowserDetailsComponent → EntityDetailViewComponent → DataMappingListComponent.
13772
+ */
13773
+ expressionValidator = input(undefined, ...(ngDevMode ? [{ debugName: "expressionValidator" }] : /* istanbul ignore next */ []));
12980
13774
  resolvedMessages = computed(() => ({
12981
13775
  ...DEFAULT_RUNTIME_BROWSER_MESSAGES,
12982
13776
  ...this.messages(),
@@ -13538,7 +14332,7 @@ class RuntimeBrowserComponent {
13538
14332
  }
13539
14333
  }
13540
14334
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RuntimeBrowserComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13541
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: RuntimeBrowserComponent, isStandalone: true, selector: "mm-runtime-browser", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, showDataMapping: { classPropertyName: "showDataMapping", publicName: "showDataMapping", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "treeDetail", first: true, predicate: ["treeDetail"], descendants: true }, { propertyName: "detailsPanel", first: true, predicate: ["detailsPanel"], descendants: true }], ngImport: i0, template: `
14335
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: RuntimeBrowserComponent, isStandalone: true, selector: "mm-runtime-browser", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, showDataMapping: { classPropertyName: "showDataMapping", publicName: "showDataMapping", isSignal: true, isRequired: false, transformFunction: null }, expressionValidator: { classPropertyName: "expressionValidator", publicName: "expressionValidator", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "treeDetail", first: true, predicate: ["treeDetail"], descendants: true }, { propertyName: "detailsPanel", first: true, predicate: ["detailsPanel"], descendants: true }], ngImport: i0, template: `
13542
14336
  <div class="runtime-browser-container kendo-theme-provider">
13543
14337
  <!-- LCARS Header -->
13544
14338
  <div class="lcars-page-header">
@@ -13580,6 +14374,7 @@ class RuntimeBrowserComponent {
13580
14374
  [selectedItem]="selectedItem"
13581
14375
  [messages]="resolvedMessages()"
13582
14376
  [showDataMapping]="showDataMapping()"
14377
+ [expressionValidator]="expressionValidator()"
13583
14378
  (entitySaved)="onEntitySaved($event)"
13584
14379
  >
13585
14380
  </mm-runtime-browser-details>
@@ -13599,7 +14394,7 @@ class RuntimeBrowserComponent {
13599
14394
  </div>
13600
14395
  </div>
13601
14396
  </div>
13602
- `, isInline: true, styles: [".runtime-browser-container{display:flex;flex-direction:column;height:100%;padding:16px;gap:16px}::ng-deep mm-base-tree-detail .k-splitter{background:transparent;border:none}::ng-deep mm-base-tree-detail .k-splitter .k-splitbar{background:linear-gradient(180deg,var(--octo-mint-30),transparent);width:4px!important}::ng-deep mm-base-tree-detail .k-splitter .k-splitbar:hover{background:linear-gradient(180deg,var(--octo-mint-50),var(--octo-mint-20))}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf{font-family:Roboto,sans-serif;transition:all .2s ease;border-radius:4px}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf:hover{background:var(--octo-mint-10);color:var(--octo-mint)}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf.k-selected{background:linear-gradient(90deg,var(--octo-mint-20),transparent);color:var(--octo-mint);border-left:3px solid var(--octo-mint)}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf.k-selected:hover{background:linear-gradient(90deg,var(--octo-mint-25),transparent)}::ng-deep mm-base-tree-detail .toolbar{background:linear-gradient(90deg,var(--octo-mint-05),transparent);border-bottom:1px solid var(--octo-mint-20)}::ng-deep mm-base-tree-detail .base-tree-detail-container{height:100%;display:flex;flex-direction:column}::ng-deep mm-base-tree-detail .base-tree-detail-container .k-splitter{flex:1;min-height:0}::ng-deep mm-base-tree-detail kendo-splitter-pane.detail-pane{display:flex!important;flex-direction:column!important;height:100%!important}::ng-deep mm-base-tree-detail kendo-splitter-pane.detail-pane>mm-runtime-browser-details{display:flex!important;flex:1!important;min-height:0!important;height:100%!important}::ng-deep mm-base-tree-detail kendo-splitter-pane.detail-pane>mm-runtime-browser-details>.runtime-browser-details{flex:1;display:flex;flex-direction:column;min-height:0}@media(max-width:1024px){.runtime-browser-container{padding:12px;gap:12px}}@media(max-width:768px){.runtime-browser-container{padding:8px;gap:10px}}\n"], dependencies: [{ kind: "component", type: BaseTreeDetailComponent, selector: "mm-base-tree-detail", inputs: ["treeDataSource", "leftPaneSize", "leftToolbarActions", "rightToolbarActions"], outputs: ["nodeSelected", "nodeDropped"] }, { kind: "component", type: RuntimeBrowserDetailsComponent, selector: "mm-runtime-browser-details", inputs: ["selectedItem", "showDataMapping", "messages"], outputs: ["entitySaved"] }] });
14397
+ `, isInline: true, styles: [".runtime-browser-container{display:flex;flex-direction:column;height:100%;padding:16px;gap:16px}::ng-deep mm-base-tree-detail .k-splitter{background:transparent;border:none}::ng-deep mm-base-tree-detail .k-splitter .k-splitbar{background:linear-gradient(180deg,var(--octo-mint-30),transparent);width:4px!important}::ng-deep mm-base-tree-detail .k-splitter .k-splitbar:hover{background:linear-gradient(180deg,var(--octo-mint-50),var(--octo-mint-20))}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf{font-family:Roboto,sans-serif;transition:all .2s ease;border-radius:4px}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf:hover{background:var(--octo-mint-10);color:var(--octo-mint)}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf.k-selected{background:linear-gradient(90deg,var(--octo-mint-20),transparent);color:var(--octo-mint);border-left:3px solid var(--octo-mint)}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf.k-selected:hover{background:linear-gradient(90deg,var(--octo-mint-25),transparent)}::ng-deep mm-base-tree-detail .toolbar{background:linear-gradient(90deg,var(--octo-mint-05),transparent);border-bottom:1px solid var(--octo-mint-20)}::ng-deep mm-base-tree-detail .base-tree-detail-container{height:100%;display:flex;flex-direction:column}::ng-deep mm-base-tree-detail .base-tree-detail-container .k-splitter{flex:1;min-height:0}::ng-deep mm-base-tree-detail kendo-splitter-pane.detail-pane{display:flex!important;flex-direction:column!important;height:100%!important}::ng-deep mm-base-tree-detail kendo-splitter-pane.detail-pane>mm-runtime-browser-details{display:flex!important;flex:1!important;min-height:0!important;height:100%!important}::ng-deep mm-base-tree-detail kendo-splitter-pane.detail-pane>mm-runtime-browser-details>.runtime-browser-details{flex:1;display:flex;flex-direction:column;min-height:0}@media(max-width:1024px){.runtime-browser-container{padding:12px;gap:12px}}@media(max-width:768px){.runtime-browser-container{padding:8px;gap:10px}}\n"], dependencies: [{ kind: "component", type: BaseTreeDetailComponent, selector: "mm-base-tree-detail", inputs: ["treeDataSource", "leftPaneSize", "leftToolbarActions", "rightToolbarActions"], outputs: ["nodeSelected", "nodeDropped"] }, { kind: "component", type: RuntimeBrowserDetailsComponent, selector: "mm-runtime-browser-details", inputs: ["selectedItem", "showDataMapping", "messages", "expressionValidator"], outputs: ["entitySaved"] }] });
13603
14398
  }
13604
14399
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RuntimeBrowserComponent, decorators: [{
13605
14400
  type: Component,
@@ -13645,6 +14440,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
13645
14440
  [selectedItem]="selectedItem"
13646
14441
  [messages]="resolvedMessages()"
13647
14442
  [showDataMapping]="showDataMapping()"
14443
+ [expressionValidator]="expressionValidator()"
13648
14444
  (entitySaved)="onEntitySaved($event)"
13649
14445
  >
13650
14446
  </mm-runtime-browser-details>
@@ -13665,7 +14461,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
13665
14461
  </div>
13666
14462
  </div>
13667
14463
  `, styles: [".runtime-browser-container{display:flex;flex-direction:column;height:100%;padding:16px;gap:16px}::ng-deep mm-base-tree-detail .k-splitter{background:transparent;border:none}::ng-deep mm-base-tree-detail .k-splitter .k-splitbar{background:linear-gradient(180deg,var(--octo-mint-30),transparent);width:4px!important}::ng-deep mm-base-tree-detail .k-splitter .k-splitbar:hover{background:linear-gradient(180deg,var(--octo-mint-50),var(--octo-mint-20))}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf{font-family:Roboto,sans-serif;transition:all .2s ease;border-radius:4px}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf:hover{background:var(--octo-mint-10);color:var(--octo-mint)}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf.k-selected{background:linear-gradient(90deg,var(--octo-mint-20),transparent);color:var(--octo-mint);border-left:3px solid var(--octo-mint)}::ng-deep mm-base-tree-detail kendo-treeview .k-treeview-item .k-treeview-leaf.k-selected:hover{background:linear-gradient(90deg,var(--octo-mint-25),transparent)}::ng-deep mm-base-tree-detail .toolbar{background:linear-gradient(90deg,var(--octo-mint-05),transparent);border-bottom:1px solid var(--octo-mint-20)}::ng-deep mm-base-tree-detail .base-tree-detail-container{height:100%;display:flex;flex-direction:column}::ng-deep mm-base-tree-detail .base-tree-detail-container .k-splitter{flex:1;min-height:0}::ng-deep mm-base-tree-detail kendo-splitter-pane.detail-pane{display:flex!important;flex-direction:column!important;height:100%!important}::ng-deep mm-base-tree-detail kendo-splitter-pane.detail-pane>mm-runtime-browser-details{display:flex!important;flex:1!important;min-height:0!important;height:100%!important}::ng-deep mm-base-tree-detail kendo-splitter-pane.detail-pane>mm-runtime-browser-details>.runtime-browser-details{flex:1;display:flex;flex-direction:column;min-height:0}@media(max-width:1024px){.runtime-browser-container{padding:12px;gap:12px}}@media(max-width:768px){.runtime-browser-container{padding:8px;gap:10px}}\n"] }]
13668
- }], propDecorators: { messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], showDataMapping: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDataMapping", required: false }] }], treeDetail: [{
14464
+ }], propDecorators: { messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], showDataMapping: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDataMapping", required: false }] }], expressionValidator: [{ type: i0.Input, args: [{ isSignal: true, alias: "expressionValidator", required: false }] }], treeDetail: [{
13669
14465
  type: ViewChild,
13670
14466
  args: ['treeDetail', { static: false }]
13671
14467
  }], detailsPanel: [{
@@ -13709,7 +14505,7 @@ class RuntimeBrowserPageComponent {
13709
14505
  ? this.injectedMessages
13710
14506
  : DEFAULT_RUNTIME_BROWSER_MESSAGES;
13711
14507
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RuntimeBrowserPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13712
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: RuntimeBrowserPageComponent, isStandalone: true, selector: "mm-runtime-browser-page", ngImport: i0, template: `<mm-runtime-browser [messages]="messages" />`, isInline: true, dependencies: [{ kind: "component", type: RuntimeBrowserComponent, selector: "mm-runtime-browser", inputs: ["messages", "showDataMapping"] }] });
14508
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: RuntimeBrowserPageComponent, isStandalone: true, selector: "mm-runtime-browser-page", ngImport: i0, template: `<mm-runtime-browser [messages]="messages" />`, isInline: true, dependencies: [{ kind: "component", type: RuntimeBrowserComponent, selector: "mm-runtime-browser", inputs: ["messages", "showDataMapping", "expressionValidator"] }] });
13713
14509
  }
13714
14510
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RuntimeBrowserPageComponent, decorators: [{
13715
14511
  type: Component,
@@ -13778,6 +14574,630 @@ function createRuntimeBrowserRoutes(options) {
13778
14574
  return [mainRoute, entityRoute];
13779
14575
  }
13780
14576
 
14577
+ const GetDataPointMappingsDocumentDto = gql `
14578
+ query getDataPointMappings($ckTypeId: String!, $after: String, $first: Int, $fieldFilters: [FieldFilter], $sort: [Sort]) {
14579
+ runtime {
14580
+ runtimeEntities(
14581
+ ckId: $ckTypeId
14582
+ after: $after
14583
+ first: $first
14584
+ fieldFilter: $fieldFilters
14585
+ sortOrder: $sort
14586
+ ) {
14587
+ totalCount
14588
+ pageInfo {
14589
+ endCursor
14590
+ hasNextPage
14591
+ }
14592
+ items {
14593
+ rtId
14594
+ ckTypeId
14595
+ rtWellKnownName
14596
+ attributes(resolveEnumValuesToNames: true) {
14597
+ items {
14598
+ attributeName
14599
+ value
14600
+ }
14601
+ }
14602
+ associations {
14603
+ definitions(direction: OUTBOUND, first: 10) {
14604
+ items {
14605
+ targetRtId
14606
+ targetCkTypeId
14607
+ originRtId
14608
+ originCkTypeId
14609
+ ckAssociationRoleId
14610
+ }
14611
+ }
14612
+ }
14613
+ }
14614
+ }
14615
+ }
14616
+ }
14617
+ `;
14618
+ class GetDataPointMappingsDtoGQL extends i1$3.Query {
14619
+ document = GetDataPointMappingsDocumentDto;
14620
+ constructor(apollo) {
14621
+ super(apollo);
14622
+ }
14623
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetDataPointMappingsDtoGQL, deps: [{ token: i1$3.Apollo }], target: i0.ɵɵFactoryTarget.Injectable });
14624
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetDataPointMappingsDtoGQL, providedIn: 'root' });
14625
+ }
14626
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GetDataPointMappingsDtoGQL, decorators: [{
14627
+ type: Injectable,
14628
+ args: [{
14629
+ providedIn: 'root'
14630
+ }]
14631
+ }], ctorParameters: () => [{ type: i1$3.Apollo }] });
14632
+
14633
+ const DATA_POINT_MAPPING_CK_TYPE = 'System.Communication/DataPointMapping';
14634
+ class DataMappingOverviewComponent {
14635
+ getMappingsGQL = inject(GetDataPointMappingsDtoGQL);
14636
+ getEntityByIdGQL = inject(GetRuntimeEntityByIdDtoGQL);
14637
+ updateEntitiesGQL = inject(UpdateRuntimeEntitiesDtoGQL);
14638
+ deleteEntitiesGQL = inject(DeleteEntitiesDtoGQL);
14639
+ navigateToEntity = new EventEmitter();
14640
+ // State
14641
+ mappings = signal([], ...(ngDevMode ? [{ debugName: "mappings" }] : /* istanbul ignore next */ []));
14642
+ loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
14643
+ selectedMapping = signal(null, ...(ngDevMode ? [{ debugName: "selectedMapping" }] : /* istanbul ignore next */ []));
14644
+ pageSize = 20;
14645
+ skip = 0;
14646
+ gridData = computed(() => ({
14647
+ data: this.mappings().slice(this.skip, this.skip + this.pageSize),
14648
+ total: this.mappings().length,
14649
+ }), ...(ngDevMode ? [{ debugName: "gridData" }] : /* istanbul ignore next */ []));
14650
+ summary = computed(() => {
14651
+ const items = this.mappings();
14652
+ return {
14653
+ total: items.length,
14654
+ valid: items.filter(m => m.validationStatus === 'valid').length,
14655
+ warnings: items.filter(m => m.validationStatus === 'warning').length,
14656
+ errors: items.filter(m => m.validationStatus === 'error').length,
14657
+ disabled: items.filter(m => !m.enabled).length,
14658
+ };
14659
+ }, ...(ngDevMode ? [{ debugName: "summary" }] : /* istanbul ignore next */ []));
14660
+ refreshIcon = arrowRotateCwIcon;
14661
+ checkIcon = checkCircleIcon;
14662
+ warnIcon = exclamationCircleIcon;
14663
+ errorIcon = xCircleIcon;
14664
+ deleteIcon = trashIcon;
14665
+ ngOnInit() {
14666
+ this.loadMappings();
14667
+ }
14668
+ async loadMappings() {
14669
+ this.loading.set(true);
14670
+ try {
14671
+ const result = await firstValueFrom(this.getMappingsGQL.fetch({
14672
+ variables: {
14673
+ ckTypeId: DATA_POINT_MAPPING_CK_TYPE,
14674
+ first: 500,
14675
+ },
14676
+ fetchPolicy: 'network-only',
14677
+ }));
14678
+ const entities = result.data?.runtime?.runtimeEntities?.items ?? [];
14679
+ const items = entities
14680
+ .filter((e) => e != null)
14681
+ .map(entity => this.mapEntityToOverviewItem(entity));
14682
+ // Run validation
14683
+ items.forEach(item => this.validateMapping(item, items));
14684
+ this.mappings.set(items);
14685
+ // Resolve entity names in background (non-blocking)
14686
+ this.resolveEntityNames(items);
14687
+ }
14688
+ catch (err) {
14689
+ console.error('Error loading DataPointMappings:', err);
14690
+ }
14691
+ finally {
14692
+ this.loading.set(false);
14693
+ }
14694
+ }
14695
+ onPageChange(event) {
14696
+ this.skip = event.skip;
14697
+ this.pageSize = event.take;
14698
+ }
14699
+ onCellClick(event) {
14700
+ this.selectedMapping.set(event.dataItem);
14701
+ }
14702
+ async onToggleEnabled(item, enabled) {
14703
+ try {
14704
+ await firstValueFrom(this.updateEntitiesGQL.mutate({
14705
+ variables: {
14706
+ entities: [{
14707
+ rtId: item.rtId,
14708
+ item: {
14709
+ ckTypeId: DATA_POINT_MAPPING_CK_TYPE,
14710
+ attributes: [{ attributeName: 'Enabled', value: enabled }],
14711
+ },
14712
+ }],
14713
+ },
14714
+ }));
14715
+ item.enabled = enabled;
14716
+ this.mappings.set([...this.mappings()]);
14717
+ }
14718
+ catch (err) {
14719
+ console.error('Error toggling mapping enabled state:', err);
14720
+ }
14721
+ }
14722
+ async onDeleteMapping(item) {
14723
+ try {
14724
+ await firstValueFrom(this.deleteEntitiesGQL.mutate({
14725
+ variables: {
14726
+ rtEntityIds: [{ rtId: item.rtId, ckTypeId: DATA_POINT_MAPPING_CK_TYPE }],
14727
+ deleteStrategy: DeleteStrategiesDto.EraseDto,
14728
+ },
14729
+ }));
14730
+ const updated = this.mappings().filter(m => m.rtId !== item.rtId);
14731
+ this.mappings.set(updated);
14732
+ if (this.selectedMapping()?.rtId === item.rtId) {
14733
+ this.selectedMapping.set(null);
14734
+ }
14735
+ }
14736
+ catch (err) {
14737
+ console.error('Error deleting mapping:', err);
14738
+ }
14739
+ }
14740
+ mapEntityToOverviewItem(entity) {
14741
+ const attrs = entity.attributes?.items ?? [];
14742
+ const assocs = entity.associations?.definitions?.items ?? [];
14743
+ const getAttr = (name) => attrs.find(a => a?.attributeName === name)?.value ?? '';
14744
+ const mapsFrom = assocs.find(a => a && String(a.ckAssociationRoleId).includes('MapsFrom'));
14745
+ const mapsTo = assocs.find(a => a && String(a.ckAssociationRoleId).includes('MapsTo'));
14746
+ const enabledRaw = attrs.find(a => a?.attributeName === 'Enabled')?.value;
14747
+ const enabled = enabledRaw === true || enabledRaw === 'true' || enabledRaw === 'True';
14748
+ return {
14749
+ rtId: String(entity.rtId),
14750
+ name: getAttr('Name') || `Mapping ${String(entity.rtId).slice(-6)}`,
14751
+ enabled,
14752
+ sourceAttributePath: getAttr('SourceAttributePath'),
14753
+ mappingExpression: getAttr('MappingExpression'),
14754
+ targetAttributePath: getAttr('TargetAttributePath'),
14755
+ sourceRtId: mapsFrom ? String(mapsFrom.targetRtId) : '',
14756
+ sourceCkTypeId: mapsFrom ? String(mapsFrom.targetCkTypeId) : '',
14757
+ sourceName: '',
14758
+ targetRtId: mapsTo ? String(mapsTo.targetRtId) : '',
14759
+ targetCkTypeId: mapsTo ? String(mapsTo.targetCkTypeId) : '',
14760
+ targetName: '',
14761
+ validationStatus: 'valid',
14762
+ validationMessages: [],
14763
+ };
14764
+ }
14765
+ validateMapping(item, allItems) {
14766
+ const messages = [];
14767
+ // Error: No source entity
14768
+ if (!item.sourceRtId) {
14769
+ messages.push({ level: 'error', code: 'SOURCE_MISSING', message: 'No MapsFrom source entity configured' });
14770
+ }
14771
+ // Error: No target entity
14772
+ if (!item.targetRtId) {
14773
+ messages.push({ level: 'error', code: 'TARGET_MISSING', message: 'No MapsTo target entity configured' });
14774
+ }
14775
+ // Error: No target attribute
14776
+ if (!item.targetAttributePath) {
14777
+ messages.push({ level: 'error', code: 'TARGET_ATTR_MISSING', message: 'TargetAttributePath is not set' });
14778
+ }
14779
+ // Warning: Duplicate mapping (same source → same target + attribute)
14780
+ const duplicates = allItems.filter(m => m.rtId !== item.rtId &&
14781
+ m.sourceRtId === item.sourceRtId &&
14782
+ m.sourceAttributePath === item.sourceAttributePath &&
14783
+ m.targetRtId === item.targetRtId &&
14784
+ m.targetAttributePath === item.targetAttributePath);
14785
+ if (duplicates.length > 0) {
14786
+ messages.push({ level: 'warning', code: 'DUPLICATE', message: 'Duplicate mapping — same source and target attribute' });
14787
+ }
14788
+ item.validationMessages = messages;
14789
+ item.validationStatus = messages.some(m => m.level === 'error')
14790
+ ? 'error'
14791
+ : messages.some(m => m.level === 'warning')
14792
+ ? 'warning'
14793
+ : 'valid';
14794
+ }
14795
+ /**
14796
+ * Resolves entity names for all unique source/target rtIds.
14797
+ * Updates items in-place and triggers signal update.
14798
+ */
14799
+ async resolveEntityNames(items) {
14800
+ // Collect unique entity references (rtId + ckTypeId pairs)
14801
+ const entityRefs = new Map();
14802
+ for (const item of items) {
14803
+ if (item.sourceRtId && item.sourceCkTypeId) {
14804
+ entityRefs.set(item.sourceRtId, { rtId: item.sourceRtId, ckTypeId: item.sourceCkTypeId });
14805
+ }
14806
+ if (item.targetRtId && item.targetCkTypeId) {
14807
+ entityRefs.set(item.targetRtId, { rtId: item.targetRtId, ckTypeId: item.targetCkTypeId });
14808
+ }
14809
+ }
14810
+ // Load entity names in parallel
14811
+ const nameMap = new Map();
14812
+ const loadPromises = [...entityRefs.values()].map(async (ref) => {
14813
+ try {
14814
+ const result = await firstValueFrom(this.getEntityByIdGQL.fetch({
14815
+ variables: { rtId: ref.rtId, ckTypeId: ref.ckTypeId },
14816
+ }));
14817
+ const entity = result.data?.runtime?.runtimeEntities?.items?.[0];
14818
+ if (entity) {
14819
+ const nameAttr = entity.attributes?.items?.find(a => a?.attributeName === 'Name');
14820
+ const name = nameAttr?.value
14821
+ ?? entity.rtWellKnownName
14822
+ ?? '';
14823
+ if (name) {
14824
+ nameMap.set(ref.rtId, name);
14825
+ }
14826
+ }
14827
+ }
14828
+ catch {
14829
+ // Entity not found or access denied — leave name empty
14830
+ }
14831
+ });
14832
+ await Promise.all(loadPromises);
14833
+ // Update items with resolved names
14834
+ let changed = false;
14835
+ for (const item of items) {
14836
+ const sourceName = nameMap.get(item.sourceRtId);
14837
+ if (sourceName && sourceName !== item.sourceName) {
14838
+ item.sourceName = sourceName;
14839
+ changed = true;
14840
+ }
14841
+ const targetName = nameMap.get(item.targetRtId);
14842
+ if (targetName && targetName !== item.targetName) {
14843
+ item.targetName = targetName;
14844
+ changed = true;
14845
+ }
14846
+ }
14847
+ // Trigger signal update to refresh the grid
14848
+ if (changed) {
14849
+ this.mappings.set([...items]);
14850
+ }
14851
+ }
14852
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataMappingOverviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14853
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DataMappingOverviewComponent, isStandalone: true, selector: "mm-data-mapping-overview", outputs: { navigateToEntity: "navigateToEntity" }, ngImport: i0, template: `
14854
+ <div class="mapping-overview">
14855
+ <!-- Summary Bar -->
14856
+ <div class="summary-bar">
14857
+ <div class="summary-item">
14858
+ <span class="summary-count">{{ summary().total }}</span>
14859
+ <span class="summary-label">Total</span>
14860
+ </div>
14861
+ <div class="summary-item summary-valid">
14862
+ <span class="summary-count">{{ summary().valid }}</span>
14863
+ <span class="summary-label">Valid</span>
14864
+ </div>
14865
+ <div class="summary-item summary-warnings">
14866
+ <span class="summary-count">{{ summary().warnings }}</span>
14867
+ <span class="summary-label">Warnings</span>
14868
+ </div>
14869
+ <div class="summary-item summary-errors">
14870
+ <span class="summary-count">{{ summary().errors }}</span>
14871
+ <span class="summary-label">Errors</span>
14872
+ </div>
14873
+ <div class="summary-item summary-disabled">
14874
+ <span class="summary-count">{{ summary().disabled }}</span>
14875
+ <span class="summary-label">Disabled</span>
14876
+ </div>
14877
+ <div class="summary-spacer"></div>
14878
+ <button kendoButton fillMode="flat" size="small" [svgIcon]="refreshIcon"
14879
+ (click)="loadMappings()">Refresh</button>
14880
+ </div>
14881
+
14882
+ <!-- Mapping Grid -->
14883
+ <kendo-grid
14884
+ [data]="gridData()"
14885
+ [pageSize]="pageSize"
14886
+ [skip]="skip"
14887
+ [pageable]="{ buttonCount: 3, pageSizes: [10, 20, 50] }"
14888
+ [sortable]="true"
14889
+ [height]="500"
14890
+ (pageChange)="onPageChange($event)"
14891
+ (cellClick)="onCellClick($event)"
14892
+ >
14893
+ <kendo-grid-column title="" field="validationStatus" [width]="40" [sortable]="false">
14894
+ <ng-template kendoGridCellTemplate let-dataItem>
14895
+ @if (dataItem.validationStatus === 'valid') {
14896
+ <kendo-svgicon [icon]="checkIcon" class="status-valid"></kendo-svgicon>
14897
+ } @else if (dataItem.validationStatus === 'warning') {
14898
+ <kendo-svgicon [icon]="warnIcon" class="status-warning"></kendo-svgicon>
14899
+ } @else {
14900
+ <kendo-svgicon [icon]="errorIcon" class="status-error"></kendo-svgicon>
14901
+ }
14902
+ </ng-template>
14903
+ </kendo-grid-column>
14904
+
14905
+ <kendo-grid-column title="Mapping Name" field="name" [width]="200">
14906
+ </kendo-grid-column>
14907
+
14908
+ <kendo-grid-column title="Source" field="sourceName" [width]="200">
14909
+ <ng-template kendoGridCellTemplate let-dataItem>
14910
+ <span class="entity-ref" (click)="navigateToEntity.emit({ rtId: dataItem.sourceRtId, ckTypeId: dataItem.sourceCkTypeId })">
14911
+ {{ dataItem.sourceName || dataItem.sourceRtId | slice:-8 }}
14912
+ </span>
14913
+ <div class="entity-type-hint">{{ dataItem.sourceCkTypeId }}</div>
14914
+ </ng-template>
14915
+ </kendo-grid-column>
14916
+
14917
+ <kendo-grid-column title="Source Attr" field="sourceAttributePath" [width]="120">
14918
+ </kendo-grid-column>
14919
+
14920
+ <kendo-grid-column title="Expression" field="mappingExpression" [width]="160">
14921
+ <ng-template kendoGridCellTemplate let-dataItem>
14922
+ @if (dataItem.mappingExpression) {
14923
+ <code class="expression-cell">{{ dataItem.mappingExpression }}</code>
14924
+ }
14925
+ </ng-template>
14926
+ </kendo-grid-column>
14927
+
14928
+ <kendo-grid-column title="Target" field="targetName" [width]="200">
14929
+ <ng-template kendoGridCellTemplate let-dataItem>
14930
+ @if (dataItem.targetRtId) {
14931
+ <span class="entity-ref" (click)="navigateToEntity.emit({ rtId: dataItem.targetRtId, ckTypeId: dataItem.targetCkTypeId })">
14932
+ {{ dataItem.targetName || dataItem.targetRtId | slice:-8 }}
14933
+ </span>
14934
+ <div class="entity-type-hint">{{ dataItem.targetCkTypeId }}</div>
14935
+ } @else {
14936
+ <span class="no-target">(no target)</span>
14937
+ }
14938
+ </ng-template>
14939
+ </kendo-grid-column>
14940
+
14941
+ <kendo-grid-column title="Target Attr" field="targetAttributePath" [width]="120">
14942
+ </kendo-grid-column>
14943
+
14944
+ <kendo-grid-column title="Enabled" field="enabled" [width]="80">
14945
+ <ng-template kendoGridCellTemplate let-dataItem>
14946
+ <kendo-switch [checked]="dataItem.enabled" size="small"
14947
+ (valueChange)="onToggleEnabled(dataItem, $event)">
14948
+ </kendo-switch>
14949
+ </ng-template>
14950
+ </kendo-grid-column>
14951
+
14952
+ <kendo-grid-column title="" [width]="50" [sortable]="false">
14953
+ <ng-template kendoGridCellTemplate let-dataItem>
14954
+ <button kendoButton fillMode="flat" size="small" [svgIcon]="deleteIcon"
14955
+ (click)="onDeleteMapping(dataItem); $event.stopPropagation()">
14956
+ </button>
14957
+ </ng-template>
14958
+ </kendo-grid-column>
14959
+ </kendo-grid>
14960
+
14961
+ <!-- Detail Panel (selected mapping) -->
14962
+ @if (selectedMapping()) {
14963
+ <div class="detail-panel">
14964
+ <h4>{{ selectedMapping()!.name || 'Mapping Details' }}</h4>
14965
+ <div class="detail-grid">
14966
+ <div class="detail-row">
14967
+ <label>Source Entity</label>
14968
+ <span class="entity-ref" (click)="navigateToEntity.emit({ rtId: selectedMapping()!.sourceRtId, ckTypeId: selectedMapping()!.sourceCkTypeId })">
14969
+ {{ selectedMapping()!.sourceName || selectedMapping()!.sourceRtId }}
14970
+ </span>
14971
+ <span class="entity-type-hint">{{ selectedMapping()!.sourceCkTypeId }}</span>
14972
+ </div>
14973
+ <div class="detail-row">
14974
+ <label>Source Attribute</label>
14975
+ <span>{{ selectedMapping()!.sourceAttributePath || '(default)' }}</span>
14976
+ </div>
14977
+ <div class="detail-row">
14978
+ <label>Expression</label>
14979
+ <code>{{ selectedMapping()!.mappingExpression || '(none — pass through)' }}</code>
14980
+ </div>
14981
+ <div class="detail-row">
14982
+ <label>Target Entity</label>
14983
+ @if (selectedMapping()!.targetRtId) {
14984
+ <span class="entity-ref" (click)="navigateToEntity.emit({ rtId: selectedMapping()!.targetRtId, ckTypeId: selectedMapping()!.targetCkTypeId })">
14985
+ {{ selectedMapping()!.targetName || selectedMapping()!.targetRtId }}
14986
+ </span>
14987
+ <span class="entity-type-hint">{{ selectedMapping()!.targetCkTypeId }}</span>
14988
+ } @else {
14989
+ <span class="no-target">(no target configured)</span>
14990
+ }
14991
+ </div>
14992
+ <div class="detail-row">
14993
+ <label>Target Attribute</label>
14994
+ <span>{{ selectedMapping()!.targetAttributePath || '(not set)' }}</span>
14995
+ </div>
14996
+ <div class="detail-row">
14997
+ <label>Enabled</label>
14998
+ <span>{{ selectedMapping()!.enabled ? 'Yes' : 'No' }}</span>
14999
+ </div>
15000
+ </div>
15001
+
15002
+ @if (selectedMapping()!.validationMessages.length > 0) {
15003
+ <div class="validation-section">
15004
+ <h5>Validation</h5>
15005
+ @for (msg of selectedMapping()!.validationMessages; track msg.code) {
15006
+ <div class="validation-msg" [class]="'validation-' + msg.level">
15007
+ {{ msg.message }}
15008
+ </div>
15009
+ }
15010
+ </div>
15011
+ }
15012
+ </div>
15013
+ }
15014
+
15015
+ @if (loading()) {
15016
+ <div class="loading-overlay">Loading mappings...</div>
15017
+ }
15018
+ </div>
15019
+ `, isInline: true, styles: [".mapping-overview{display:flex;flex-direction:column;gap:12px;padding:16px}.summary-bar{display:flex;gap:16px;align-items:center;padding:8px 12px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:6px;background:var(--kendo-color-surface-alt, #f8f9fa)}.summary-item{display:flex;flex-direction:column;align-items:center;min-width:60px}.summary-count{font-size:1.2rem;font-weight:700}.summary-label{font-size:.7rem;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d)}.summary-valid .summary-count{color:var(--kendo-color-success, #28a745)}.summary-warnings .summary-count{color:var(--kendo-color-warning, #ffc107)}.summary-errors .summary-count{color:var(--kendo-color-error, #dc3545)}.summary-disabled .summary-count{color:var(--kendo-color-subtle, #6c757d)}.summary-spacer{flex:1}.status-valid{color:var(--kendo-color-success, #28a745)}.status-warning{color:var(--kendo-color-warning, #ffc107)}.status-error{color:var(--kendo-color-error, #dc3545)}.entity-ref{cursor:pointer;color:var(--kendo-color-primary, #ff6358);font-family:monospace;font-size:.85rem}.entity-ref:hover{text-decoration:underline}.no-target{color:var(--kendo-color-subtle, #6c757d);font-style:italic}.entity-type-hint{font-size:.7rem;color:var(--kendo-color-subtle, #6c757d);font-family:monospace}.expression-cell{font-size:.85rem;background:var(--kendo-color-surface-alt, #f8f9fa);padding:2px 4px;border-radius:3px}.detail-panel{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:6px;padding:12px 16px}.detail-panel h4{margin:0 0 12px;font-size:.9rem;font-weight:600}.detail-grid{display:grid;grid-template-columns:120px 1fr;gap:6px 12px}.detail-row{display:contents}.detail-row label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d);padding-top:2px}.detail-row span,.detail-row code{font-size:.85rem}.validation-section{margin-top:12px;padding-top:8px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.validation-section h5{margin:0 0 6px;font-size:.8rem;font-weight:600}.validation-msg{font-size:.8rem;padding:2px 0}.validation-error{color:var(--kendo-color-error, #dc3545)}.validation-warning{color:var(--kendo-color-warning, #ffc107)}.validation-info{color:var(--kendo-color-info, #17a2b8)}.loading-overlay{text-align:center;padding:20px;color:var(--kendo-color-subtle, #6c757d)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i5$1.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }, { kind: "ngmodule", type: GridModule }, { kind: "component", type: i3.GridComponent, selector: "kendo-grid", inputs: ["data", "pageSize", "height", "rowHeight", "adaptiveMode", "detailRowHeight", "skip", "scrollable", "selectable", "sort", "size", "trackBy", "filter", "group", "virtualColumns", "filterable", "sortable", "pageable", "groupable", "gridResizable", "rowReorderable", "navigable", "autoSize", "rowClass", "rowSticky", "rowSelected", "isRowSelectable", "cellSelected", "resizable", "reorderable", "loading", "columnMenu", "hideHeader", "showInactiveTools", "isDetailExpanded", "isGroupExpanded", "dataLayoutMode"], outputs: ["filterChange", "pageChange", "groupChange", "sortChange", "selectionChange", "rowReorder", "dataStateChange", "gridStateChange", "groupExpand", "groupCollapse", "detailExpand", "detailCollapse", "edit", "cancel", "save", "remove", "add", "cellClose", "cellClick", "pdfExport", "excelExport", "columnResize", "columnReorder", "columnVisibilityChange", "columnLockedChange", "columnStickyChange", "scrollBottom", "contentScroll"], exportAs: ["kendoGrid"] }, { kind: "component", type: i3.ColumnComponent, selector: "kendo-grid-column", inputs: ["field", "format", "sortable", "groupable", "editor", "filter", "filterVariant", "filterable", "editable"] }, { kind: "directive", type: i3.CellTemplateDirective, selector: "[kendoGridCellTemplate]" }, { kind: "ngmodule", type: BadgeModule }, { kind: "ngmodule", type: SwitchModule }, { kind: "component", type: i5.SwitchComponent, selector: "kendo-switch", inputs: ["focusableId", "onLabel", "offLabel", "checked", "disabled", "readonly", "tabindex", "size", "thumbRounded", "trackRounded", "tabIndex"], outputs: ["focus", "blur", "valueChange"], exportAs: ["kendoSwitch"] }, { kind: "pipe", type: i1$2.SlicePipe, name: "slice" }] });
15020
+ }
15021
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataMappingOverviewComponent, decorators: [{
15022
+ type: Component,
15023
+ args: [{ selector: 'mm-data-mapping-overview', standalone: true, imports: [
15024
+ CommonModule,
15025
+ ButtonModule,
15026
+ SVGIconModule,
15027
+ GridModule,
15028
+ BadgeModule,
15029
+ SwitchModule,
15030
+ ], template: `
15031
+ <div class="mapping-overview">
15032
+ <!-- Summary Bar -->
15033
+ <div class="summary-bar">
15034
+ <div class="summary-item">
15035
+ <span class="summary-count">{{ summary().total }}</span>
15036
+ <span class="summary-label">Total</span>
15037
+ </div>
15038
+ <div class="summary-item summary-valid">
15039
+ <span class="summary-count">{{ summary().valid }}</span>
15040
+ <span class="summary-label">Valid</span>
15041
+ </div>
15042
+ <div class="summary-item summary-warnings">
15043
+ <span class="summary-count">{{ summary().warnings }}</span>
15044
+ <span class="summary-label">Warnings</span>
15045
+ </div>
15046
+ <div class="summary-item summary-errors">
15047
+ <span class="summary-count">{{ summary().errors }}</span>
15048
+ <span class="summary-label">Errors</span>
15049
+ </div>
15050
+ <div class="summary-item summary-disabled">
15051
+ <span class="summary-count">{{ summary().disabled }}</span>
15052
+ <span class="summary-label">Disabled</span>
15053
+ </div>
15054
+ <div class="summary-spacer"></div>
15055
+ <button kendoButton fillMode="flat" size="small" [svgIcon]="refreshIcon"
15056
+ (click)="loadMappings()">Refresh</button>
15057
+ </div>
15058
+
15059
+ <!-- Mapping Grid -->
15060
+ <kendo-grid
15061
+ [data]="gridData()"
15062
+ [pageSize]="pageSize"
15063
+ [skip]="skip"
15064
+ [pageable]="{ buttonCount: 3, pageSizes: [10, 20, 50] }"
15065
+ [sortable]="true"
15066
+ [height]="500"
15067
+ (pageChange)="onPageChange($event)"
15068
+ (cellClick)="onCellClick($event)"
15069
+ >
15070
+ <kendo-grid-column title="" field="validationStatus" [width]="40" [sortable]="false">
15071
+ <ng-template kendoGridCellTemplate let-dataItem>
15072
+ @if (dataItem.validationStatus === 'valid') {
15073
+ <kendo-svgicon [icon]="checkIcon" class="status-valid"></kendo-svgicon>
15074
+ } @else if (dataItem.validationStatus === 'warning') {
15075
+ <kendo-svgicon [icon]="warnIcon" class="status-warning"></kendo-svgicon>
15076
+ } @else {
15077
+ <kendo-svgicon [icon]="errorIcon" class="status-error"></kendo-svgicon>
15078
+ }
15079
+ </ng-template>
15080
+ </kendo-grid-column>
15081
+
15082
+ <kendo-grid-column title="Mapping Name" field="name" [width]="200">
15083
+ </kendo-grid-column>
15084
+
15085
+ <kendo-grid-column title="Source" field="sourceName" [width]="200">
15086
+ <ng-template kendoGridCellTemplate let-dataItem>
15087
+ <span class="entity-ref" (click)="navigateToEntity.emit({ rtId: dataItem.sourceRtId, ckTypeId: dataItem.sourceCkTypeId })">
15088
+ {{ dataItem.sourceName || dataItem.sourceRtId | slice:-8 }}
15089
+ </span>
15090
+ <div class="entity-type-hint">{{ dataItem.sourceCkTypeId }}</div>
15091
+ </ng-template>
15092
+ </kendo-grid-column>
15093
+
15094
+ <kendo-grid-column title="Source Attr" field="sourceAttributePath" [width]="120">
15095
+ </kendo-grid-column>
15096
+
15097
+ <kendo-grid-column title="Expression" field="mappingExpression" [width]="160">
15098
+ <ng-template kendoGridCellTemplate let-dataItem>
15099
+ @if (dataItem.mappingExpression) {
15100
+ <code class="expression-cell">{{ dataItem.mappingExpression }}</code>
15101
+ }
15102
+ </ng-template>
15103
+ </kendo-grid-column>
15104
+
15105
+ <kendo-grid-column title="Target" field="targetName" [width]="200">
15106
+ <ng-template kendoGridCellTemplate let-dataItem>
15107
+ @if (dataItem.targetRtId) {
15108
+ <span class="entity-ref" (click)="navigateToEntity.emit({ rtId: dataItem.targetRtId, ckTypeId: dataItem.targetCkTypeId })">
15109
+ {{ dataItem.targetName || dataItem.targetRtId | slice:-8 }}
15110
+ </span>
15111
+ <div class="entity-type-hint">{{ dataItem.targetCkTypeId }}</div>
15112
+ } @else {
15113
+ <span class="no-target">(no target)</span>
15114
+ }
15115
+ </ng-template>
15116
+ </kendo-grid-column>
15117
+
15118
+ <kendo-grid-column title="Target Attr" field="targetAttributePath" [width]="120">
15119
+ </kendo-grid-column>
15120
+
15121
+ <kendo-grid-column title="Enabled" field="enabled" [width]="80">
15122
+ <ng-template kendoGridCellTemplate let-dataItem>
15123
+ <kendo-switch [checked]="dataItem.enabled" size="small"
15124
+ (valueChange)="onToggleEnabled(dataItem, $event)">
15125
+ </kendo-switch>
15126
+ </ng-template>
15127
+ </kendo-grid-column>
15128
+
15129
+ <kendo-grid-column title="" [width]="50" [sortable]="false">
15130
+ <ng-template kendoGridCellTemplate let-dataItem>
15131
+ <button kendoButton fillMode="flat" size="small" [svgIcon]="deleteIcon"
15132
+ (click)="onDeleteMapping(dataItem); $event.stopPropagation()">
15133
+ </button>
15134
+ </ng-template>
15135
+ </kendo-grid-column>
15136
+ </kendo-grid>
15137
+
15138
+ <!-- Detail Panel (selected mapping) -->
15139
+ @if (selectedMapping()) {
15140
+ <div class="detail-panel">
15141
+ <h4>{{ selectedMapping()!.name || 'Mapping Details' }}</h4>
15142
+ <div class="detail-grid">
15143
+ <div class="detail-row">
15144
+ <label>Source Entity</label>
15145
+ <span class="entity-ref" (click)="navigateToEntity.emit({ rtId: selectedMapping()!.sourceRtId, ckTypeId: selectedMapping()!.sourceCkTypeId })">
15146
+ {{ selectedMapping()!.sourceName || selectedMapping()!.sourceRtId }}
15147
+ </span>
15148
+ <span class="entity-type-hint">{{ selectedMapping()!.sourceCkTypeId }}</span>
15149
+ </div>
15150
+ <div class="detail-row">
15151
+ <label>Source Attribute</label>
15152
+ <span>{{ selectedMapping()!.sourceAttributePath || '(default)' }}</span>
15153
+ </div>
15154
+ <div class="detail-row">
15155
+ <label>Expression</label>
15156
+ <code>{{ selectedMapping()!.mappingExpression || '(none — pass through)' }}</code>
15157
+ </div>
15158
+ <div class="detail-row">
15159
+ <label>Target Entity</label>
15160
+ @if (selectedMapping()!.targetRtId) {
15161
+ <span class="entity-ref" (click)="navigateToEntity.emit({ rtId: selectedMapping()!.targetRtId, ckTypeId: selectedMapping()!.targetCkTypeId })">
15162
+ {{ selectedMapping()!.targetName || selectedMapping()!.targetRtId }}
15163
+ </span>
15164
+ <span class="entity-type-hint">{{ selectedMapping()!.targetCkTypeId }}</span>
15165
+ } @else {
15166
+ <span class="no-target">(no target configured)</span>
15167
+ }
15168
+ </div>
15169
+ <div class="detail-row">
15170
+ <label>Target Attribute</label>
15171
+ <span>{{ selectedMapping()!.targetAttributePath || '(not set)' }}</span>
15172
+ </div>
15173
+ <div class="detail-row">
15174
+ <label>Enabled</label>
15175
+ <span>{{ selectedMapping()!.enabled ? 'Yes' : 'No' }}</span>
15176
+ </div>
15177
+ </div>
15178
+
15179
+ @if (selectedMapping()!.validationMessages.length > 0) {
15180
+ <div class="validation-section">
15181
+ <h5>Validation</h5>
15182
+ @for (msg of selectedMapping()!.validationMessages; track msg.code) {
15183
+ <div class="validation-msg" [class]="'validation-' + msg.level">
15184
+ {{ msg.message }}
15185
+ </div>
15186
+ }
15187
+ </div>
15188
+ }
15189
+ </div>
15190
+ }
15191
+
15192
+ @if (loading()) {
15193
+ <div class="loading-overlay">Loading mappings...</div>
15194
+ }
15195
+ </div>
15196
+ `, styles: [".mapping-overview{display:flex;flex-direction:column;gap:12px;padding:16px}.summary-bar{display:flex;gap:16px;align-items:center;padding:8px 12px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:6px;background:var(--kendo-color-surface-alt, #f8f9fa)}.summary-item{display:flex;flex-direction:column;align-items:center;min-width:60px}.summary-count{font-size:1.2rem;font-weight:700}.summary-label{font-size:.7rem;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d)}.summary-valid .summary-count{color:var(--kendo-color-success, #28a745)}.summary-warnings .summary-count{color:var(--kendo-color-warning, #ffc107)}.summary-errors .summary-count{color:var(--kendo-color-error, #dc3545)}.summary-disabled .summary-count{color:var(--kendo-color-subtle, #6c757d)}.summary-spacer{flex:1}.status-valid{color:var(--kendo-color-success, #28a745)}.status-warning{color:var(--kendo-color-warning, #ffc107)}.status-error{color:var(--kendo-color-error, #dc3545)}.entity-ref{cursor:pointer;color:var(--kendo-color-primary, #ff6358);font-family:monospace;font-size:.85rem}.entity-ref:hover{text-decoration:underline}.no-target{color:var(--kendo-color-subtle, #6c757d);font-style:italic}.entity-type-hint{font-size:.7rem;color:var(--kendo-color-subtle, #6c757d);font-family:monospace}.expression-cell{font-size:.85rem;background:var(--kendo-color-surface-alt, #f8f9fa);padding:2px 4px;border-radius:3px}.detail-panel{border:1px solid var(--kendo-color-border, #dee2e6);border-radius:6px;padding:12px 16px}.detail-panel h4{margin:0 0 12px;font-size:.9rem;font-weight:600}.detail-grid{display:grid;grid-template-columns:120px 1fr;gap:6px 12px}.detail-row{display:contents}.detail-row label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d);padding-top:2px}.detail-row span,.detail-row code{font-size:.85rem}.validation-section{margin-top:12px;padding-top:8px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.validation-section h5{margin:0 0 6px;font-size:.8rem;font-weight:600}.validation-msg{font-size:.8rem;padding:2px 0}.validation-error{color:var(--kendo-color-error, #dc3545)}.validation-warning{color:var(--kendo-color-warning, #ffc107)}.validation-info{color:var(--kendo-color-info, #17a2b8)}.loading-overlay{text-align:center;padding:20px;color:var(--kendo-color-subtle, #6c757d)}\n"] }]
15197
+ }], propDecorators: { navigateToEntity: [{
15198
+ type: Output
15199
+ }] } });
15200
+
13781
15201
  class TenantSwitcherComponent {
13782
15202
  currentTenantId = null;
13783
15203
  allowedTenants = [];
@@ -13955,5 +15375,5 @@ function provideOctoUi() {
13955
15375
  * Generated bundle index. Do not edit.
13956
15376
  */
13957
15377
 
13958
- export { AssociationValidationService, AttributeSelectorDialogComponent, AttributeSelectorDialogService, AttributeSortSelectorDialogComponent, AttributeSortSelectorDialogService, AttributeValueTypeDto, CkTypeSelectorDialogComponent, CkTypeSelectorDialogService, CkTypeSelectorInputComponent, DEFAULT_RUNTIME_BROWSER_MESSAGES, DefaultPropertyCategory, EntityDetailComponent, EntityIdInfoComponent, EntitySelectorDialogComponent, EntitySelectorDialogService, FieldFilterEditorComponent, OctoGraphQlDataSource, OctoGraphQlHierarchyDataSource, OctoLoaderComponent, PropertyConverterService, PropertyDisplayMode, PropertyGridComponent, PropertyValueDisplayComponent, RUNTIME_BROWSER_MESSAGES, RtEntityIdHelper, RuntimeBrowserComponent, RuntimeBrowserOutletComponent, RuntimeBrowserPageComponent, RuntimeBrowserStateService, RuntimeEntityVariableDialogComponent, RuntimeEntityVariableDialogService, TenantSwitcherComponent, account_tree, add, analytics, app_registration, article, botService, category, chat, checklist, code, component_exchange, computer, createRuntimeBrowserRoutes, customer, dashboard, event_list, graphic_eq, group, identityService, insert_link, manage_accounts, more_time, notifications, page_info, pages, person_search, playlist_add_check, pool, power, provideOctoUi, publicIcon, query_builder, schedule_send, settings, sort, storage, swagger, swagger_asset, swagger_bot, swagger_communication, swagger_identity, team_dashboard, tenancy, text_snippet, travel_explore, user_diagnostics, webhook, work };
15378
+ export { AssociationValidationService, AttributeSelectorDialogComponent, AttributeSelectorDialogService, AttributeSortSelectorDialogComponent, AttributeSortSelectorDialogService, AttributeValueTypeDto, CkTypeSelectorDialogComponent, CkTypeSelectorDialogService, CkTypeSelectorInputComponent, DEFAULT_RUNTIME_BROWSER_MESSAGES, DataMappingOverviewComponent, DefaultPropertyCategory, EntityDetailComponent, EntityIdInfoComponent, EntitySelectorDialogComponent, EntitySelectorDialogService, FieldFilterEditorComponent, OctoGraphQlDataSource, OctoGraphQlHierarchyDataSource, OctoLoaderComponent, PropertyConverterService, PropertyDisplayMode, PropertyGridComponent, PropertyValueDisplayComponent, RUNTIME_BROWSER_MESSAGES, RecordDetailDialogComponent, RtEntityIdHelper, RuntimeBrowserComponent, RuntimeBrowserOutletComponent, RuntimeBrowserPageComponent, RuntimeBrowserStateService, RuntimeEntityVariableDialogComponent, RuntimeEntityVariableDialogService, TenantSwitcherComponent, account_tree, add, analytics, app_registration, article, botService, category, chat, checklist, code, component_exchange, computer, createRuntimeBrowserRoutes, customer, dashboard, event_list, graphic_eq, group, identityService, insert_link, manage_accounts, more_time, notifications, page_info, pages, person_search, playlist_add_check, pool, power, provideOctoUi, publicIcon, query_builder, schedule_send, settings, sort, storage, swagger, swagger_asset, swagger_bot, swagger_communication, swagger_identity, team_dashboard, tenancy, text_snippet, travel_explore, user_diagnostics, webhook, work };
13959
15379
  //# sourceMappingURL=meshmakers-octo-ui.mjs.map