@memberjunction/ng-entity-viewer 6.2.0-edge.0 → 6.2.0-edge.1

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.
Files changed (67) hide show
  1. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.d.ts +22 -0
  2. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.d.ts.map +1 -1
  3. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.js +57 -13
  4. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.js.map +1 -1
  5. package/dist/lib/entity-cards/entity-cards.component.d.ts +67 -5
  6. package/dist/lib/entity-cards/entity-cards.component.d.ts.map +1 -1
  7. package/dist/lib/entity-cards/entity-cards.component.js +202 -71
  8. package/dist/lib/entity-cards/entity-cards.component.js.map +1 -1
  9. package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts +159 -16
  10. package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts.map +1 -1
  11. package/dist/lib/entity-data-grid/entity-data-grid.component.js +446 -147
  12. package/dist/lib/entity-data-grid/entity-data-grid.component.js.map +1 -1
  13. package/dist/lib/entity-data-grid/events/grid-events.js +67 -67
  14. package/dist/lib/entity-data-grid/events/grid-events.js.map +1 -1
  15. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.d.ts +112 -8
  16. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.d.ts.map +1 -1
  17. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.js +245 -61
  18. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.js.map +1 -1
  19. package/dist/lib/entity-viewer/entity-viewer.component.d.ts +6 -0
  20. package/dist/lib/entity-viewer/entity-viewer.component.d.ts.map +1 -1
  21. package/dist/lib/entity-viewer/entity-viewer.component.js +28 -9
  22. package/dist/lib/entity-viewer/entity-viewer.component.js.map +1 -1
  23. package/dist/lib/pill/pill.component.d.ts +13 -3
  24. package/dist/lib/pill/pill.component.d.ts.map +1 -1
  25. package/dist/lib/pill/pill.component.js +34 -10
  26. package/dist/lib/pill/pill.component.js.map +1 -1
  27. package/dist/lib/quick-save-dialog/quick-save-dialog.component.d.ts +29 -3
  28. package/dist/lib/quick-save-dialog/quick-save-dialog.component.d.ts.map +1 -1
  29. package/dist/lib/quick-save-dialog/quick-save-dialog.component.js +173 -114
  30. package/dist/lib/quick-save-dialog/quick-save-dialog.component.js.map +1 -1
  31. package/dist/lib/recycle-bin/recycle-bin.component.d.ts +4 -0
  32. package/dist/lib/recycle-bin/recycle-bin.component.d.ts.map +1 -1
  33. package/dist/lib/recycle-bin/recycle-bin.component.js +10 -2
  34. package/dist/lib/recycle-bin/recycle-bin.component.js.map +1 -1
  35. package/dist/lib/utils/highlight.util.d.ts +6 -0
  36. package/dist/lib/utils/highlight.util.d.ts.map +1 -1
  37. package/dist/lib/utils/highlight.util.js +23 -11
  38. package/dist/lib/utils/highlight.util.js.map +1 -1
  39. package/dist/lib/utils/record.util.d.ts +10 -0
  40. package/dist/lib/utils/record.util.d.ts.map +1 -1
  41. package/dist/lib/utils/record.util.js +20 -4
  42. package/dist/lib/utils/record.util.js.map +1 -1
  43. package/dist/lib/view-config-panel/view-config-panel.component.d.ts +17 -1
  44. package/dist/lib/view-config-panel/view-config-panel.component.d.ts.map +1 -1
  45. package/dist/lib/view-config-panel/view-config-panel.component.js +39 -6
  46. package/dist/lib/view-config-panel/view-config-panel.component.js.map +1 -1
  47. package/dist/lib/view-selector/view-selector.component.js +2 -2
  48. package/dist/lib/view-types/renderers/cards-view-renderer.component.d.ts +15 -3
  49. package/dist/lib/view-types/renderers/cards-view-renderer.component.d.ts.map +1 -1
  50. package/dist/lib/view-types/renderers/cards-view-renderer.component.js +33 -5
  51. package/dist/lib/view-types/renderers/cards-view-renderer.component.js.map +1 -1
  52. package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts +70 -2
  53. package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts.map +1 -1
  54. package/dist/lib/view-types/renderers/grid-view-renderer.component.js +155 -33
  55. package/dist/lib/view-types/renderers/grid-view-renderer.component.js.map +1 -1
  56. package/dist/lib/view-types/renderers/map-view-renderer.component.d.ts +16 -2
  57. package/dist/lib/view-types/renderers/map-view-renderer.component.d.ts.map +1 -1
  58. package/dist/lib/view-types/renderers/map-view-renderer.component.js +37 -9
  59. package/dist/lib/view-types/renderers/map-view-renderer.component.js.map +1 -1
  60. package/dist/lib/view-types/view-type.contracts.d.ts +8 -0
  61. package/dist/lib/view-types/view-type.contracts.d.ts.map +1 -1
  62. package/dist/lib/view-types/view-type.contracts.js.map +1 -1
  63. package/dist/lib/view-workspace/view-workspace.component.d.ts +243 -25
  64. package/dist/lib/view-workspace/view-workspace.component.d.ts.map +1 -1
  65. package/dist/lib/view-workspace/view-workspace.component.js +631 -180
  66. package/dist/lib/view-workspace/view-workspace.component.js.map +1 -1
  67. package/package.json +21 -20
@@ -6,7 +6,8 @@ import { debounceTime, takeUntil } from 'rxjs/operators';
6
6
  import { LogError, RunView, RunViewParams, CoerceImageSrc, ParseCssHexColor, CompositeKey, IsDateOnlySQLType, FormatDateOnly, EntityFieldTSType } from '@memberjunction/core';
7
7
  import { UUIDsEqual } from '@memberjunction/global';
8
8
  import { EntityActionEngineBase } from '@memberjunction/actions-base';
9
- import { buildPkString, canonicalizeColumnFields, computeFieldsList } from '../utils/record.util';
9
+ import { CommunicationEngineBase } from '@memberjunction/communication-types';
10
+ import { BuildPkString, CanonicalizeColumnFields, ComputeFieldsList } from '../utils/record.util';
10
11
  import { AggregateField } from '../utils/aggregate-field.util';
11
12
  import { ViewInfo, UserViewEngine, UserInfoEngine } from '@memberjunction/core-entities';
12
13
  import { ModuleRegistry, AllCommunityModule, themeAlpine } from 'ag-grid-community';
@@ -456,7 +457,7 @@ function EntityDataGridComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
456
457
  i0.ɵɵadvance();
457
458
  i0.ɵɵconditional(ctx_r1.ShowDuplicateSearchButton ? 17 : -1);
458
459
  i0.ɵɵadvance();
459
- i0.ɵɵconditional(ctx_r1.ShowCommunicationButton ? 18 : -1);
460
+ i0.ɵɵconditional(ctx_r1.ShowCommunicationButton && ctx_r1.EntitySupportsCommunication ? 18 : -1);
460
461
  i0.ɵɵadvance();
461
462
  i0.ɵɵconditional(ctx_r1.ToolbarConfig.showAdd && ctx_r1.AllowAdd && !ctx_r1.ShowNewButton ? 19 : -1);
462
463
  i0.ɵɵadvance();
@@ -535,7 +536,7 @@ function EntityDataGridComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
535
536
  i0.ɵɵelement(0, "mj-empty-state", 6);
536
537
  } if (rf & 2) {
537
538
  const ctx_r1 = i0.ɵɵnextContext();
538
- i0.ɵɵproperty("Size", ctx_r1.EmptyStateSize);
539
+ i0.ɵɵproperty("Size", ctx_r1.EmptyStateSize)("Title", ctx_r1.EntityInfo ? "No " + ctx_r1.EntityInfo.DisplayNamePlural + " to display" : "No data to display");
539
540
  } }
540
541
  function EntityDataGridComponent_Conditional_8_Template(rf, ctx) { if (rf & 1) {
541
542
  const _r30 = i0.ɵɵgetCurrentView();
@@ -654,6 +655,8 @@ ModuleRegistry.registerModules([AllCommunityModule]);
654
655
  const AUTO_WIDTH_COLUMN_MIN_PX = 160;
655
656
  /** Column id of the inert width-filler appended when `FillWidth` is on. See `buildFillerColumnDef`. */
656
657
  const FILLER_COLUMN_ID = '__mjFill';
658
+ /** Field (and column id) of the row-number column added when `ShowRowNumbers` is on. */
659
+ const ROW_NUMBER_FIELD = '__rowNumber';
657
660
  /**
658
661
  * Flex weight given to a `width: 'auto'` column so it outranks the filler when leftover row width is
659
662
  * shared out. Any value that dwarfs the filler's weight of 1 works; the effect is "this column takes
@@ -793,9 +796,13 @@ export class EntityDataGridComponent extends BaseAngularComponent {
793
796
  * Convenience accessor for the resolved entity name. Returns null when
794
797
  * the entity hasn't been resolved yet (e.g., before data loads).
795
798
  */
796
- get entityInfoName() {
799
+ get EntityInfoName() {
797
800
  return this._entityInfo?.Name ?? null;
798
801
  }
802
+ /** @deprecated Use {@link EntityInfoName}. */
803
+ get entityInfoName() {
804
+ return this.EntityInfoName;
805
+ }
799
806
  /**
800
807
  * Current page number for the shared pager (1-based).
801
808
  * Set by parent when using external data with server-side paging.
@@ -1148,9 +1155,13 @@ export class EntityDataGridComponent extends BaseAngularComponent {
1148
1155
  /**
1149
1156
  * Get the effective visual config (user config merged with defaults)
1150
1157
  */
1151
- get effectiveVisualConfig() {
1158
+ get EffectiveVisualConfig() {
1152
1159
  return { ...DEFAULT_VISUAL_CONFIG, ...this._visualConfig };
1153
1160
  }
1161
+ /** @deprecated Use {@link EffectiveVisualConfig}. */
1162
+ get effectiveVisualConfig() {
1163
+ return this.EffectiveVisualConfig;
1164
+ }
1154
1165
  // ========================================
1155
1166
  // Toolbar Inputs
1156
1167
  // ========================================
@@ -1325,6 +1336,45 @@ export class EntityDataGridComponent extends BaseAngularComponent {
1325
1336
  get ShowCommunicationButton() {
1326
1337
  return this._showCommunicationButton;
1327
1338
  }
1339
+ _entitySupportsCommunication = false;
1340
+ _communicationCheckedForEntityID = null;
1341
+ /**
1342
+ * True when the current entity has at least one active Entity Communication Message Type. "Send
1343
+ * Message" is offered — as a toolbar button or in the overflow menu — only when this is true, so an
1344
+ * entity with no communication setup never shows a control that cannot do anything.
1345
+ */
1346
+ get EntitySupportsCommunication() {
1347
+ return this._entitySupportsCommunication;
1348
+ }
1349
+ /** Resolves {@link EntitySupportsCommunication} for the current entity, once per entity. */
1350
+ async resolveCommunicationSupport() {
1351
+ const entity = this._entityInfo;
1352
+ if (!entity) {
1353
+ this._entitySupportsCommunication = false;
1354
+ this._communicationCheckedForEntityID = null;
1355
+ return;
1356
+ }
1357
+ if (this._communicationCheckedForEntityID && UUIDsEqual(this._communicationCheckedForEntityID, entity.ID)) {
1358
+ return;
1359
+ }
1360
+ this._communicationCheckedForEntityID = entity.ID;
1361
+ this._entitySupportsCommunication = false;
1362
+ try {
1363
+ const provider = this.ProviderToUse;
1364
+ const engine = CommunicationEngineBase.GetProviderInstance(provider, CommunicationEngineBase);
1365
+ await engine.Config(false, provider.CurrentUser, provider);
1366
+ if (this._entityInfo !== entity) {
1367
+ return; // the grid moved to another entity while the engine loaded
1368
+ }
1369
+ this._entitySupportsCommunication = engine.Metadata.EntityCommunicationMessageTypes
1370
+ .some(m => m.IsActive && UUIDsEqual(m.EntityID, entity.ID));
1371
+ this.cdr.detectChanges();
1372
+ }
1373
+ catch {
1374
+ // Non-fatal: leave Send Message hidden, and retry on the next entity resolution.
1375
+ this._communicationCheckedForEntityID = null;
1376
+ }
1377
+ }
1328
1378
  // ========================================
1329
1379
  // Navigation Inputs
1330
1380
  // ========================================
@@ -1426,8 +1476,9 @@ export class EntityDataGridComponent extends BaseAngularComponent {
1426
1476
  this._entityActionsAutoLoaded = true;
1427
1477
  try {
1428
1478
  const provider = this.ProviderToUse;
1429
- await EntityActionEngineBase.Instance.Config(false, provider?.CurrentUser, provider);
1430
- const configs = this.mapEntityActionsToConfigs(this._entityInfo.Name);
1479
+ const engine = this.entityActionEngine();
1480
+ await engine.Config(false, provider.CurrentUser, provider);
1481
+ const configs = this.mapEntityActionsToConfigs(engine, this._entityInfo.Name);
1431
1482
  if (configs.length > 0) {
1432
1483
  this._entityActions = configs;
1433
1484
  this._showEntityActionButtons = true;
@@ -1439,9 +1490,12 @@ export class EntityDataGridComponent extends BaseAngularComponent {
1439
1490
  this._entityActionsAutoLoaded = false;
1440
1491
  }
1441
1492
  }
1493
+ /** The EntityAction engine for this grid's provider, so a multi-provider client reads the right server's actions. */
1494
+ entityActionEngine() {
1495
+ return EntityActionEngineBase.GetProviderInstance(this.ProviderToUse, EntityActionEngineBase);
1496
+ }
1442
1497
  /** Maps the entity's active EntityActions to grid configs, carrying the driver key + RecordProcessID. */
1443
- mapEntityActionsToConfigs(entityName) {
1444
- const engine = EntityActionEngineBase.Instance;
1498
+ mapEntityActionsToConfigs(engine, entityName) {
1445
1499
  return engine.GetActionsByEntityName(entityName, 'Active').map((ea) => {
1446
1500
  const driverInvocation = engine.Invocations.find(i => UUIDsEqual(i.EntityActionID, ea.ID) && !!i.RuntimeUXDriverClass);
1447
1501
  const recordProcessParam = engine.Params.find(p => UUIDsEqual(p.EntityActionID, ea.ID) && p.ActionParam?.trim().toLowerCase() === 'recordprocessid');
@@ -1495,14 +1549,14 @@ export class EntityDataGridComponent extends BaseAngularComponent {
1495
1549
  * Returns the effective aggregates config, preferring _aggregatesConfig but falling back to _gridState.aggregates.
1496
1550
  * This ensures aggregates work regardless of whether they came from explicit config or from view's GridState.
1497
1551
  */
1498
- get EffectiveAggregatesConfig() {
1552
+ get effectiveAggregatesConfig() {
1499
1553
  return this._aggregatesConfig || this._gridState?.aggregates;
1500
1554
  }
1501
1555
  /**
1502
1556
  * Returns enabled aggregates configured for card display.
1503
1557
  */
1504
1558
  get CardAggregates() {
1505
- const config = this.EffectiveAggregatesConfig;
1559
+ const config = this.effectiveAggregatesConfig;
1506
1560
  if (!config?.expressions)
1507
1561
  return [];
1508
1562
  return config.expressions
@@ -1513,7 +1567,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
1513
1567
  * Returns enabled aggregates configured for column footer display.
1514
1568
  */
1515
1569
  get ColumnAggregates() {
1516
- const config = this.EffectiveAggregatesConfig;
1570
+ const config = this.effectiveAggregatesConfig;
1517
1571
  if (!config?.expressions)
1518
1572
  return [];
1519
1573
  return config.expressions
@@ -1537,7 +1591,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
1537
1591
  /**
1538
1592
  * Get formatted aggregate value for display
1539
1593
  */
1540
- getAggregateValue(agg) {
1594
+ GetAggregateValue(agg) {
1541
1595
  const key = agg.id || agg.expression;
1542
1596
  const value = this._aggregateValues.get(key);
1543
1597
  if (value == null)
@@ -1567,6 +1621,10 @@ export class EntityDataGridComponent extends BaseAngularComponent {
1567
1621
  }
1568
1622
  return String(value);
1569
1623
  }
1624
+ /** @deprecated Use {@link GetAggregateValue}. */
1625
+ getAggregateValue(agg) {
1626
+ return this.GetAggregateValue(agg);
1627
+ }
1570
1628
  /**
1571
1629
  * Whether an aggregate summarises a date-only column. An aggregate carries no field metadata of
1572
1630
  * its own, so the column is read from a single-field expression such as `MIN(IntakeDate)` or
@@ -1702,18 +1760,42 @@ export class EntityDataGridComponent extends BaseAngularComponent {
1702
1760
  // ========================================
1703
1761
  // View Children
1704
1762
  // ========================================
1705
- gridContainer;
1763
+ GridContainer;
1764
+ /** @deprecated Use {@link GridContainer}. */
1765
+ get gridContainer() {
1766
+ return this.GridContainer;
1767
+ }
1768
+ /** @deprecated Use {@link GridContainer}. */
1769
+ set gridContainer(value) {
1770
+ this.GridContainer = value;
1771
+ }
1706
1772
  // ========================================
1707
1773
  // AG Grid Properties
1708
1774
  // ========================================
1709
1775
  /** AG Grid column definitions */
1710
- agColumnDefs = [];
1776
+ AgColumnDefs = [];
1777
+ /** @deprecated Use {@link AgColumnDefs}. */
1778
+ get agColumnDefs() {
1779
+ return this.AgColumnDefs;
1780
+ }
1781
+ /** @deprecated Use {@link AgColumnDefs}. */
1782
+ set agColumnDefs(value) {
1783
+ this.AgColumnDefs = value;
1784
+ }
1711
1785
  /** AG Grid row data */
1712
- rowData = [];
1786
+ RowData = [];
1787
+ /** @deprecated Use {@link RowData}. */
1788
+ get rowData() {
1789
+ return this.RowData;
1790
+ }
1791
+ /** @deprecated Use {@link RowData}. */
1792
+ set rowData(value) {
1793
+ this.RowData = value;
1794
+ }
1713
1795
  /** AG Grid API reference */
1714
1796
  gridApi = null;
1715
1797
  /** AG Grid theme (v34+) with custom selection colors */
1716
- agGridTheme = themeAlpine.withParams({
1798
+ AgGridTheme = themeAlpine.withParams({
1717
1799
  backgroundColor: 'var(--mj-bg-surface)',
1718
1800
  foregroundColor: 'var(--mj-text-primary)',
1719
1801
  textColor: 'var(--mj-text-primary)',
@@ -1731,23 +1813,47 @@ export class EntityDataGridComponent extends BaseAngularComponent {
1731
1813
  borderRadius: 'var(--mj-radius-sm)',
1732
1814
  browserColorScheme: 'inherit',
1733
1815
  });
1816
+ /** @deprecated Use {@link AgGridTheme}. */
1817
+ get agGridTheme() {
1818
+ return this.AgGridTheme;
1819
+ }
1820
+ /** @deprecated Use {@link AgGridTheme}. */
1821
+ set agGridTheme(value) {
1822
+ this.AgGridTheme = value;
1823
+ }
1734
1824
  /** AG Grid row selection configuration */
1735
- agRowSelection = { mode: 'singleRow' };
1825
+ AgRowSelection = { mode: 'singleRow' };
1826
+ /** @deprecated Use {@link AgRowSelection}. */
1827
+ get agRowSelection() {
1828
+ return this.AgRowSelection;
1829
+ }
1830
+ /** @deprecated Use {@link AgRowSelection}. */
1831
+ set agRowSelection(value) {
1832
+ this.AgRowSelection = value;
1833
+ }
1736
1834
  /** Default column settings */
1737
- defaultColDef = {
1835
+ DefaultColDef = {
1738
1836
  sortable: true,
1739
1837
  filter: false,
1740
1838
  resizable: true,
1741
1839
  minWidth: 80
1742
1840
  };
1841
+ /** @deprecated Use {@link DefaultColDef}. */
1842
+ get defaultColDef() {
1843
+ return this.DefaultColDef;
1844
+ }
1845
+ /** @deprecated Use {@link DefaultColDef}. */
1846
+ set defaultColDef(value) {
1847
+ this.DefaultColDef = value;
1848
+ }
1743
1849
  /**
1744
1850
  * Update defaultColDef when text wrapping setting changes
1745
1851
  * Enables/disables auto row height and cell text wrapping
1746
1852
  */
1747
1853
  updateDefaultColDefForWrapping() {
1748
1854
  if (this._wrapText) {
1749
- this.defaultColDef = {
1750
- ...this.defaultColDef,
1855
+ this.DefaultColDef = {
1856
+ ...this.DefaultColDef,
1751
1857
  wrapText: true,
1752
1858
  autoHeight: true,
1753
1859
  cellClass: 'cell-wrap-text'
@@ -1755,21 +1861,37 @@ export class EntityDataGridComponent extends BaseAngularComponent {
1755
1861
  }
1756
1862
  else {
1757
1863
  // Remove wrapping properties
1758
- const { wrapText, autoHeight, cellClass, ...rest } = this.defaultColDef;
1759
- this.defaultColDef = rest;
1864
+ const { wrapText, autoHeight, cellClass, ...rest } = this.DefaultColDef;
1865
+ this.DefaultColDef = rest;
1760
1866
  }
1761
1867
  // Refresh the grid to apply changes
1762
1868
  if (this.gridApi) {
1763
- this.gridApi.setGridOption('defaultColDef', this.defaultColDef);
1869
+ this.gridApi.setGridOption('defaultColDef', this.DefaultColDef);
1764
1870
  this.gridApi.refreshCells({ force: true });
1765
1871
  }
1766
1872
  }
1767
1873
  /** Get row ID function for AG Grid */
1768
- getRowId = (params) => params.data['__pk'];
1874
+ GetRowId = (params) => params.data['__pk'];
1875
+ /** @deprecated Use {@link GetRowId}. */
1876
+ get getRowId() {
1877
+ return this.GetRowId;
1878
+ }
1879
+ /** @deprecated Use {@link GetRowId}. */
1880
+ set getRowId(value) {
1881
+ this.GetRowId = value;
1882
+ }
1769
1883
  /** Suppress sort changed events during programmatic updates */
1770
1884
  suppressSortEvents = false;
1771
1885
  /** AG Grid options for infinite scroll mode */
1772
- gridOptions = {};
1886
+ GridOptions = {};
1887
+ /** @deprecated Use {@link GridOptions}. */
1888
+ get gridOptions() {
1889
+ return this.GridOptions;
1890
+ }
1891
+ /** @deprecated Use {@link GridOptions}. */
1892
+ set gridOptions(value) {
1893
+ this.GridOptions = value;
1894
+ }
1773
1895
  /** Datasource for infinite scroll mode */
1774
1896
  infiniteDatasource = null;
1775
1897
  // ========================================
@@ -1824,7 +1946,15 @@ export class EntityDataGridComponent extends BaseAngularComponent {
1824
1946
  return true;
1825
1947
  }
1826
1948
  // Loading state
1827
- loading = false;
1949
+ Loading = false;
1950
+ /** @deprecated Use {@link Loading}. */
1951
+ get loading() {
1952
+ return this.Loading;
1953
+ }
1954
+ /** @deprecated Use {@link Loading}. */
1955
+ set loading(value) {
1956
+ this.Loading = value;
1957
+ }
1828
1958
  _errorMessage = '';
1829
1959
  /**
1830
1960
  * Raw technical error from the last failed load. Kept for callers and the
@@ -1849,7 +1979,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
1849
1979
  const friendly = 'The server may be busy or briefly unreachable — retrying usually fixes this.';
1850
1980
  return this._errorMessage ? `${friendly} (Detail: ${this._errorMessage})` : friendly;
1851
1981
  }
1852
- totalRowCount = 0;
1982
+ totalRowCount = 0; // case-violation-ok-legacy-back-compat: the PascalCase name is already taken in this scope
1853
1983
  _loadDataPromise = null;
1854
1984
  // Cleanup
1855
1985
  destroy$ = new Subject();
@@ -1884,17 +2014,45 @@ export class EntityDataGridComponent extends BaseAngularComponent {
1884
2014
  * For dynamic views, always true (persists to user settings).
1885
2015
  * For saved views, depends on view ownership and permissions.
1886
2016
  */
1887
- get canEditCurrentView() {
2017
+ get CanEditCurrentView() {
1888
2018
  if (this.IsDynamicView) {
1889
2019
  return true; // Dynamic views persist to user settings
1890
2020
  }
1891
2021
  return this._viewEntity?.UserCanEdit ?? false;
1892
2022
  }
2023
+ /** @deprecated Use {@link CanEditCurrentView}. */
2024
+ get canEditCurrentView() {
2025
+ return this.CanEditCurrentView;
2026
+ }
1893
2027
  // Overflow menu state
1894
- showOverflowMenu = false;
2028
+ ShowOverflowMenu = false;
2029
+ /** @deprecated Use {@link ShowOverflowMenu}. */
2030
+ get showOverflowMenu() {
2031
+ return this.ShowOverflowMenu;
2032
+ }
2033
+ /** @deprecated Use {@link ShowOverflowMenu}. */
2034
+ set showOverflowMenu(value) {
2035
+ this.ShowOverflowMenu = value;
2036
+ }
1895
2037
  // Export dialog state
1896
- showExportDialog = false;
1897
- exportDialogConfig = null;
2038
+ ShowExportDialog = false;
2039
+ /** @deprecated Use {@link ShowExportDialog}. */
2040
+ get showExportDialog() {
2041
+ return this.ShowExportDialog;
2042
+ }
2043
+ /** @deprecated Use {@link ShowExportDialog}. */
2044
+ set showExportDialog(value) {
2045
+ this.ShowExportDialog = value;
2046
+ }
2047
+ ExportDialogConfig = null;
2048
+ /** @deprecated Use {@link ExportDialogConfig}. */
2049
+ get exportDialogConfig() {
2050
+ return this.ExportDialogConfig;
2051
+ }
2052
+ /** @deprecated Use {@link ExportDialogConfig}. */
2053
+ set exportDialogConfig(value) {
2054
+ this.ExportDialogConfig = value;
2055
+ }
1898
2056
  constructor(cdr, elementRef, exportService, ngZone) {
1899
2057
  super();
1900
2058
  this.cdr = cdr;
@@ -2078,6 +2236,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
2078
2236
  this._suppressPersist = false;
2079
2237
  // Entity is resolved by now — self-load its actions if auto-load is enabled.
2080
2238
  void this.maybeAutoLoadEntityActions();
2239
+ void this.resolveCommunicationSupport();
2081
2240
  }
2082
2241
  }
2083
2242
  /**
@@ -2294,7 +2453,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
2294
2453
  this.buildAgColumnDefs();
2295
2454
  // Update AG Grid with new column definitions to apply header styles
2296
2455
  if (this.gridApi && !this.gridApi.isDestroyed()) {
2297
- this.gridApi.setGridOption('columnDefs', this.agColumnDefs);
2456
+ this.gridApi.setGridOption('columnDefs', this.AgColumnDefs);
2298
2457
  // Refresh header to apply new header styles
2299
2458
  this.gridApi.refreshHeader();
2300
2459
  }
@@ -2327,7 +2486,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
2327
2486
  if (this._gridState.aggregates) {
2328
2487
  this._aggregatesConfig = this._gridState.aggregates;
2329
2488
  // Fetch aggregate values when gridState aggregates change
2330
- this.refreshAggregates();
2489
+ this.RefreshAggregates();
2331
2490
  }
2332
2491
  // Clear suppression after AG Grid's async events have been processed.
2333
2492
  // setTimeout(0) fires after microtasks and rAF, catching all async sort events.
@@ -2339,8 +2498,8 @@ export class EntityDataGridComponent extends BaseAngularComponent {
2339
2498
  * Used when gridState changes and includes new aggregate config.
2340
2499
  * This runs a RunView with MaxRows=0 to get only aggregate results.
2341
2500
  */
2342
- async refreshAggregates() {
2343
- const effectiveAggConfig = this.EffectiveAggregatesConfig;
2501
+ async RefreshAggregates() {
2502
+ const effectiveAggConfig = this.effectiveAggregatesConfig;
2344
2503
  if (!effectiveAggConfig?.expressions?.length) {
2345
2504
  this._aggregateResults = [];
2346
2505
  this._aggregateValues.clear();
@@ -2404,13 +2563,17 @@ export class EntityDataGridComponent extends BaseAngularComponent {
2404
2563
  });
2405
2564
  }
2406
2565
  }
2566
+ /** @deprecated Use {@link RefreshAggregates}. */
2567
+ async refreshAggregates() {
2568
+ return this.RefreshAggregates();
2569
+ }
2407
2570
  onFilterTextChanged() {
2408
2571
  // Rebuild column defs to update cell renderers with new filter text
2409
2572
  this.buildAgColumnDefs();
2410
2573
  // Update AG Grid with new column definitions and refresh cells
2411
2574
  if (this.gridApi) {
2412
2575
  // Update the column definitions in AG Grid
2413
- this.gridApi.setGridOption('columnDefs', this.agColumnDefs);
2576
+ this.gridApi.setGridOption('columnDefs', this.AgColumnDefs);
2414
2577
  // Force refresh all cells to apply new highlighting
2415
2578
  this.gridApi.refreshCells({ force: true });
2416
2579
  // Apply AG Grid quick filter to actually filter rows client-side
@@ -2661,16 +2824,16 @@ export class EntityDataGridComponent extends BaseAngularComponent {
2661
2824
  ? this.buildAgColumnDefsFromGridState(this._gridState.columnSettings)
2662
2825
  : [];
2663
2826
  if (fromGridState.length > 0) {
2664
- this.agColumnDefs = fromGridState;
2827
+ this.AgColumnDefs = fromGridState;
2665
2828
  }
2666
2829
  else if (this._columns.length > 0) {
2667
- this.agColumnDefs = this._columns.map(col => this.mapColumnConfigToColDef(col));
2830
+ this.AgColumnDefs = this._columns.map(col => this.mapColumnConfigToColDef(col));
2668
2831
  }
2669
2832
  else if (this._entityInfo) {
2670
- this.agColumnDefs = this.generateAgColumnDefs(this._entityInfo);
2833
+ this.AgColumnDefs = this.generateAgColumnDefs(this._entityInfo);
2671
2834
  }
2672
2835
  else {
2673
- this.agColumnDefs = [];
2836
+ this.AgColumnDefs = [];
2674
2837
  }
2675
2838
  // Field security, applied to EVERY branch above rather than inside each one. This is the
2676
2839
  // only point all three column sources meet: a saved view's `columnSettings` (the usual path
@@ -2680,13 +2843,13 @@ export class EntityDataGridComponent extends BaseAngularComponent {
2680
2843
  // appended — those are synthetic and have no entity field to check.
2681
2844
  const deniedReadFields = this.deniedReadFieldsFor(this._entityInfo);
2682
2845
  if (deniedReadFields.size > 0) {
2683
- this.agColumnDefs = this.agColumnDefs.filter(c => !c.field || !deniedReadFields.has(c.field.trim().toLowerCase()));
2846
+ this.AgColumnDefs = this.AgColumnDefs.filter(c => !c.field || !deniedReadFields.has(c.field.trim().toLowerCase()));
2684
2847
  }
2685
2848
  // Add row number column if enabled
2686
- if (this._showRowNumbers && this.agColumnDefs.length > 0) {
2687
- this.agColumnDefs.unshift({
2849
+ if (this._showRowNumbers && this.AgColumnDefs.length > 0) {
2850
+ this.AgColumnDefs.unshift({
2688
2851
  headerName: '#',
2689
- field: '__rowNumber',
2852
+ field: ROW_NUMBER_FIELD,
2690
2853
  width: 60,
2691
2854
  minWidth: 50,
2692
2855
  maxWidth: 80,
@@ -2695,8 +2858,8 @@ export class EntityDataGridComponent extends BaseAngularComponent {
2695
2858
  valueGetter: (params) => params.node ? params.node.rowIndex + 1 : ''
2696
2859
  });
2697
2860
  }
2698
- if (this._fillWidth && this.agColumnDefs.length > 0) {
2699
- this.agColumnDefs.push(this.buildFillerColumnDef());
2861
+ if (this._fillWidth && this.AgColumnDefs.length > 0) {
2862
+ this.AgColumnDefs.push(this.buildFillerColumnDef());
2700
2863
  }
2701
2864
  }
2702
2865
  /**
@@ -2765,7 +2928,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
2765
2928
  if (!this._columnsFromHost || !this._entityInfo || this._columns.length === 0) {
2766
2929
  return false;
2767
2930
  }
2768
- const next = canonicalizeColumnFields(this._entityInfo, this._columns);
2931
+ const next = CanonicalizeColumnFields(this._entityInfo, this._columns);
2769
2932
  const changed = next.some((col, i) => col !== this._columns[i]);
2770
2933
  if (changed) {
2771
2934
  this._columns = next;
@@ -2986,7 +3149,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
2986
3149
  const fieldType = field.TSType;
2987
3150
  const fieldNameLower = field.Name.toLowerCase();
2988
3151
  const extendedType = field.ExtendedType?.toLowerCase() || '';
2989
- const vc = this.effectiveVisualConfig;
3152
+ const vc = this.EffectiveVisualConfig;
2990
3153
  // Determine special field types using ExtendedType metadata first, then field name patterns as fallback
2991
3154
  const isCurrency = customFormat?.type === 'currency' ||
2992
3155
  fieldNameLower.includes('amount') ||
@@ -3401,17 +3564,17 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3401
3564
  updateAgRowSelection() {
3402
3565
  switch (this._selectionMode) {
3403
3566
  case 'none':
3404
- this.agRowSelection = { mode: 'singleRow', enableClickSelection: false, checkboxes: false };
3567
+ this.AgRowSelection = { mode: 'singleRow', enableClickSelection: false, checkboxes: false };
3405
3568
  break;
3406
3569
  case 'single':
3407
- this.agRowSelection = { mode: 'singleRow', enableClickSelection: true, checkboxes: false };
3570
+ this.AgRowSelection = { mode: 'singleRow', enableClickSelection: true, checkboxes: false };
3408
3571
  break;
3409
3572
  case 'multiple':
3410
3573
  // enableSelectionWithoutKeys replaces deprecated rowMultiSelectWithClick (as of AG Grid v32.2)
3411
- this.agRowSelection = { mode: 'multiRow', enableClickSelection: true, checkboxes: false, enableSelectionWithoutKeys: true };
3574
+ this.AgRowSelection = { mode: 'multiRow', enableClickSelection: true, checkboxes: false, enableSelectionWithoutKeys: true };
3412
3575
  break;
3413
3576
  case 'checkbox':
3414
- this.agRowSelection = {
3577
+ this.AgRowSelection = {
3415
3578
  mode: 'multiRow',
3416
3579
  enableClickSelection: true,
3417
3580
  checkboxes: true,
@@ -3422,7 +3585,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3422
3585
  }
3423
3586
  // Update the grid if it's already initialized
3424
3587
  if (this.gridApi) {
3425
- this.gridApi.setGridOption('rowSelection', this.agRowSelection);
3588
+ this.gridApi.setGridOption('rowSelection', this.AgRowSelection);
3426
3589
  }
3427
3590
  }
3428
3591
  // ========================================
@@ -3521,7 +3684,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3521
3684
  runViewParams.UserSearchString = beforeLoadEvent.modifiedParams.searchString;
3522
3685
  }
3523
3686
  }
3524
- this.loading = true;
3687
+ this.Loading = true;
3525
3688
  this._aggregatesLoading = true;
3526
3689
  this.errorMessage = '';
3527
3690
  this.cdr.detectChanges();
@@ -3529,7 +3692,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3529
3692
  try {
3530
3693
  // Build aggregate expressions from config if present
3531
3694
  // Use EffectiveAggregatesConfig to check both _aggregatesConfig and _gridState.aggregates
3532
- const effectiveAggConfig = this.EffectiveAggregatesConfig;
3695
+ const effectiveAggConfig = this.effectiveAggregatesConfig;
3533
3696
  let aggregateExpressions;
3534
3697
  if (effectiveAggConfig?.expressions?.length) {
3535
3698
  aggregateExpressions = effectiveAggConfig.expressions
@@ -3543,7 +3706,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3543
3706
  const result = await rv.RunView({
3544
3707
  ...runViewParams,
3545
3708
  ResultType: 'simple',
3546
- Fields: this._entityInfo ? computeFieldsList(this._entityInfo, this._gridState, this.hostColumnFieldNames()) : undefined,
3709
+ Fields: this._entityInfo ? ComputeFieldsList(this._entityInfo, this._gridState, this.hostColumnFieldNames()) : undefined,
3547
3710
  Aggregates: aggregateExpressions
3548
3711
  });
3549
3712
  const loadTimeMs = performance.now() - startTime;
@@ -3580,7 +3743,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3580
3743
  }
3581
3744
  finally {
3582
3745
  this.ngZone.run(() => {
3583
- this.loading = false;
3746
+ this.Loading = false;
3584
3747
  this.cdr.detectChanges();
3585
3748
  });
3586
3749
  }
@@ -3636,7 +3799,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3636
3799
  const result = await rv.RunView({
3637
3800
  ...runViewParams,
3638
3801
  ResultType: 'simple',
3639
- Fields: this._entityInfo ? computeFieldsList(this._entityInfo, this._gridState, this.hostColumnFieldNames()) : undefined
3802
+ Fields: this._entityInfo ? ComputeFieldsList(this._entityInfo, this._gridState, this.hostColumnFieldNames()) : undefined
3640
3803
  });
3641
3804
  if (result.Success) {
3642
3805
  const entities = result.Results || [];
@@ -3729,7 +3892,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3729
3892
  processData() {
3730
3893
  this._rowDataMap.clear();
3731
3894
  const dataSource = this._useExternalData ? this._data : this._allData;
3732
- this.rowData = dataSource.map((entity, index) => {
3895
+ this.RowData = dataSource.map((entity, index) => {
3733
3896
  const key = this.getRowKey(entity);
3734
3897
  const rowData = {
3735
3898
  key,
@@ -3790,7 +3953,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3790
3953
  getRowKey(entity) {
3791
3954
  // Build key from EntityInfo PK fields when available
3792
3955
  if (this._entityInfo) {
3793
- return buildPkString(entity, this._entityInfo);
3956
+ return BuildPkString(entity, this._entityInfo);
3794
3957
  }
3795
3958
  // Fallback to configured key field via direct property access
3796
3959
  const keyValue = entity[this._keyField];
@@ -3806,7 +3969,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3806
3969
  // ========================================
3807
3970
  // AG Grid Event Handlers
3808
3971
  // ========================================
3809
- onGridReady(event) {
3972
+ OnGridReady(event) {
3810
3973
  this.gridApi = event.api;
3811
3974
  // Let the quick filter (search box) match values in HIDDEN columns too (bug C2). AG Grid
3812
3975
  // defaults `includeHiddenColumnsInQuickFilter` to false, so without this a search term that
@@ -3825,6 +3988,10 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3825
3988
  this.setupInfiniteScroll();
3826
3989
  }
3827
3990
  }
3991
+ /** @deprecated Use {@link OnGridReady}. */
3992
+ onGridReady(event) {
3993
+ return this.OnGridReady(event);
3994
+ }
3828
3995
  /**
3829
3996
  * Smart column auto-sizing that respects our width estimates
3830
3997
  * while ensuring the grid fills available space appropriately
@@ -3834,10 +4001,10 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3834
4001
  // The proportional stretch below would fight that: sizeColumnsToFit() assigns explicit widths to
3835
4002
  // every column, which clears the filler's flex and re-introduces the stretching of real columns
3836
4003
  // that the filler exists to avoid.
3837
- if (this.agColumnDefs.some(col => col.flex))
4004
+ if (this.AgColumnDefs.some(col => col.flex))
3838
4005
  return;
3839
4006
  // Get total estimated width from our column definitions
3840
- const totalEstimatedWidth = this.agColumnDefs.reduce((sum, col) => sum + (col.width || 150), 0);
4007
+ const totalEstimatedWidth = this.AgColumnDefs.reduce((sum, col) => sum + (col.width || 150), 0);
3841
4008
  // Get available width from the grid container
3842
4009
  const gridElement = this.elementRef.nativeElement.querySelector('.mj-ag-grid');
3843
4010
  const availableWidth = gridElement?.clientWidth || 0;
@@ -3858,7 +4025,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3858
4025
  // If our columns are wider than available space, let the user scroll horizontally
3859
4026
  // (AG Grid handles this automatically)
3860
4027
  }
3861
- onAgRowClicked(event) {
4028
+ OnAgRowClicked(event) {
3862
4029
  if (!this._entityInfo || !event.data)
3863
4030
  return;
3864
4031
  const pkString = event.data['__pk'];
@@ -3879,7 +4046,11 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3879
4046
  this.emitNavigationRequest(rowData.entity, pkString);
3880
4047
  }
3881
4048
  }
3882
- onAgRowDoubleClicked(event) {
4049
+ /** @deprecated Use {@link OnAgRowClicked}. */
4050
+ onAgRowClicked(event) {
4051
+ return this.OnAgRowClicked(event);
4052
+ }
4053
+ OnAgRowDoubleClicked(event) {
3883
4054
  if (!this._entityInfo || !event.data)
3884
4055
  return;
3885
4056
  const pkString = event.data['__pk'];
@@ -3900,12 +4071,16 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3900
4071
  this.emitNavigationRequest(rowData.entity, pkString);
3901
4072
  }
3902
4073
  }
4074
+ /** @deprecated Use {@link OnAgRowDoubleClicked}. */
4075
+ onAgRowDoubleClicked(event) {
4076
+ return this.OnAgRowDoubleClicked(event);
4077
+ }
3903
4078
  /**
3904
4079
  * Handles cell click events to detect FK link clicks.
3905
4080
  * When a user clicks on a foreign key link, emits ForeignKeyClick event
3906
4081
  * for the parent component to handle navigation.
3907
4082
  */
3908
- onAgCellClicked(event) {
4083
+ OnAgCellClicked(event) {
3909
4084
  // Check if the click was on an FK link
3910
4085
  const target = event.event?.target;
3911
4086
  if (!target) {
@@ -3935,6 +4110,10 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3935
4110
  //console.log('[FK Debug] Missing required data attributes, not emitting');
3936
4111
  }
3937
4112
  }
4113
+ /** @deprecated Use {@link OnAgCellClicked}. */
4114
+ onAgCellClicked(event) {
4115
+ return this.OnAgCellClicked(event);
4116
+ }
3938
4117
  /**
3939
4118
  * Emits a navigation request for the given entity record.
3940
4119
  */
@@ -3947,7 +4126,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3947
4126
  compositeKey
3948
4127
  });
3949
4128
  }
3950
- onAgSortChanged(event) {
4129
+ OnAgSortChanged(event) {
3951
4130
  if (this.suppressSortEvents) {
3952
4131
  return;
3953
4132
  }
@@ -3995,7 +4174,11 @@ export class EntityDataGridComponent extends BaseAngularComponent {
3995
4174
  this.emitGridStateChanged('sort');
3996
4175
  }
3997
4176
  }
3998
- onAgSelectionChanged(event) {
4177
+ /** @deprecated Use {@link OnAgSortChanged}. */
4178
+ onAgSortChanged(event) {
4179
+ return this.OnAgSortChanged(event);
4180
+ }
4181
+ OnAgSelectionChanged(event) {
3999
4182
  const selectedNodes = event.api.getSelectedNodes();
4000
4183
  const previousSelection = [...this._selectedKeys];
4001
4184
  const newSelection = selectedNodes.map(node => node.data['__pk']);
@@ -4039,20 +4222,32 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4039
4222
  }
4040
4223
  this.SelectionChange.emit(this._selectedKeys);
4041
4224
  }
4042
- onAgColumnResized(event) {
4225
+ /** @deprecated Use {@link OnAgSelectionChanged}. */
4226
+ onAgSelectionChanged(event) {
4227
+ return this.OnAgSelectionChanged(event);
4228
+ }
4229
+ OnAgColumnResized(event) {
4043
4230
  if (event.finished && event.source !== 'api') {
4044
4231
  // User manually resized a column - mark as dirty
4045
4232
  this._isGridStateDirty = true;
4046
4233
  this.emitGridStateChanged('columns');
4047
4234
  }
4048
4235
  }
4049
- onAgColumnMoved(event) {
4236
+ /** @deprecated Use {@link OnAgColumnResized}. */
4237
+ onAgColumnResized(event) {
4238
+ return this.OnAgColumnResized(event);
4239
+ }
4240
+ OnAgColumnMoved(event) {
4050
4241
  if (event.finished && event.source !== 'api') {
4051
4242
  // User manually moved a column - mark as dirty
4052
4243
  this._isGridStateDirty = true;
4053
4244
  this.emitGridStateChanged('columns');
4054
4245
  }
4055
4246
  }
4247
+ /** @deprecated Use {@link OnAgColumnMoved}. */
4248
+ onAgColumnMoved(event) {
4249
+ return this.OnAgColumnMoved(event);
4250
+ }
4056
4251
  // ========================================
4057
4252
  // Grid State Management
4058
4253
  // ========================================
@@ -4072,7 +4267,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4072
4267
  });
4073
4268
  // Schedule auto-persist if enabled, but NOT during view transitions
4074
4269
  // (suppressPersist prevents old view's state from being saved to new view)
4075
- if (this._autoPersistState && !this._suppressPersist && this.canEditCurrentView) {
4270
+ if (this._autoPersistState && !this._suppressPersist && this.CanEditCurrentView) {
4076
4271
  if (!this.IsDynamicView && this._viewEntity) {
4077
4272
  // Stored view - persist to UserView.GridState (debounced)
4078
4273
  // Track pending state for flush on destroy
@@ -4164,7 +4359,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4164
4359
  const sortedColumns = [];
4165
4360
  for (let i = 0; i < columnState.length; i++) {
4166
4361
  const col = columnState[i];
4167
- if (col.colId === '__rowNumber')
4362
+ if (col.colId === ROW_NUMBER_FIELD)
4168
4363
  continue; // Skip row number column
4169
4364
  // Case-insensitive: `colId` originates from a colDef's `field`, which for host-supplied
4170
4365
  // columns is whatever the page wrote. `canonicalizeHostColumns()` normally settles that
@@ -4384,7 +4579,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4384
4579
  this._data = [];
4385
4580
  this._rowDataMap.clear();
4386
4581
  this._selectedKeys = [];
4387
- this.rowData = [];
4582
+ this.RowData = [];
4388
4583
  this.totalRowCount = 0;
4389
4584
  this.cdr.detectChanges();
4390
4585
  }
@@ -4456,7 +4651,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4456
4651
  // ========================================
4457
4652
  // Toolbar Click Handlers
4458
4653
  // ========================================
4459
- onAddClick() {
4654
+ OnAddClick() {
4460
4655
  // Emit legacy events for backward compatibility
4461
4656
  this.AddRequested.emit();
4462
4657
  this.NewButtonClick.emit();
@@ -4476,19 +4671,31 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4476
4671
  }
4477
4672
  }
4478
4673
  }
4479
- onDeleteClick() {
4674
+ /** @deprecated Use {@link OnAddClick}. */
4675
+ onAddClick() {
4676
+ return this.OnAddClick();
4677
+ }
4678
+ OnDeleteClick() {
4480
4679
  const selectedRows = this.GetSelectedRows();
4481
4680
  if (selectedRows.length > 0) {
4482
4681
  this.DeleteRequested.emit(selectedRows);
4483
4682
  this.DeleteButtonClick.emit(selectedRows);
4484
4683
  }
4485
4684
  }
4486
- onExportClick() {
4685
+ /** @deprecated Use {@link OnDeleteClick}. */
4686
+ onDeleteClick() {
4687
+ return this.OnDeleteClick();
4688
+ }
4689
+ OnExportClick() {
4487
4690
  this.ExportRequested.emit();
4488
4691
  this.ExportButtonClick.emit();
4489
4692
  // Show the export dialog
4490
4693
  void this.showExportDialogForCurrentData();
4491
4694
  }
4695
+ /** @deprecated Use {@link OnExportClick}. */
4696
+ onExportClick() {
4697
+ return this.OnExportClick();
4698
+ }
4492
4699
  /**
4493
4700
  * Shows the export dialog for the full result set (falling back to the loaded page when no
4494
4701
  * ExportDataProvider is supplied).
@@ -4503,9 +4710,9 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4503
4710
  finally {
4504
4711
  this.IsPreparingExport = false;
4505
4712
  }
4506
- const columns = this.getExportColumns();
4713
+ const columns = this.GetExportColumns();
4507
4714
  const fileName = this.getDefaultExportFileName();
4508
- this.exportDialogConfig = {
4715
+ this.ExportDialogConfig = {
4509
4716
  data,
4510
4717
  columns,
4511
4718
  defaultFileName: fileName,
@@ -4515,17 +4722,21 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4515
4722
  defaultSamplingMode: 'all',
4516
4723
  dialogTitle: `Export ${this._entityInfo?.Name || 'Data'}`
4517
4724
  };
4518
- this.showExportDialog = true;
4725
+ this.ShowExportDialog = true;
4519
4726
  this.cdr.detectChanges();
4520
4727
  }
4521
4728
  /**
4522
4729
  * Handle export dialog close
4523
4730
  */
4524
- onExportDialogClosed(result) {
4525
- this.showExportDialog = false;
4526
- this.exportDialogConfig = null;
4731
+ OnExportDialogClosed(result) {
4732
+ this.ShowExportDialog = false;
4733
+ this.ExportDialogConfig = null;
4527
4734
  this.cdr.detectChanges();
4528
4735
  }
4736
+ /** @deprecated Use {@link OnExportDialogClosed}. */
4737
+ onExportDialogClosed(result) {
4738
+ return this.OnExportDialogClosed(result);
4739
+ }
4529
4740
  /**
4530
4741
  * Export grid data directly without showing dialog.
4531
4742
  * Use this for programmatic export with specific options.
@@ -4535,7 +4746,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4535
4746
  */
4536
4747
  async Export(options, download = true) {
4537
4748
  const data = await this.resolveExportData();
4538
- const columns = this.getExportColumns();
4749
+ const columns = this.GetExportColumns();
4539
4750
  const fileName = options?.fileName || this.getDefaultExportFileName();
4540
4751
  const exportOptions = {
4541
4752
  format: 'excel',
@@ -4595,39 +4806,43 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4595
4806
  }
4596
4807
  }
4597
4808
  // rowData is already plain Record<string, unknown>[] objects - return directly
4598
- return this.rowData.map(row => row);
4599
- }
4600
- /**
4601
- * Get column definitions for export based on current grid columns
4602
- */
4603
- getExportColumns() {
4604
- if (!this._entityInfo) {
4605
- // Fallback: use AG Grid column definitions
4606
- return this.agColumnDefs
4607
- .filter(col => col.field && !col.hide)
4608
- .map(col => ({
4609
- name: col.field,
4610
- displayName: (col.headerName || col.field)
4611
- }));
4612
- }
4613
- // Use entity field info for better column metadata
4614
- return this._columns
4615
- .filter(col => col.visible !== false)
4616
- .map(col => {
4809
+ return this.RowData.map(row => row);
4810
+ }
4811
+ /**
4812
+ * The columns the grid is showing, in the order it shows them, with the headers it shows. This is the
4813
+ * single source for every export path — the toolbar's Export button, {@link Export}, and hosts such as
4814
+ * the view workspace — so an export always matches the screen. It reads the rendered AG Grid columns
4815
+ * rather than the host-declared `Columns` or a saved grid state, because either of those can differ
4816
+ * from the screen in order, visibility or header text. Before the grid has rendered, it falls back to
4817
+ * the column definitions it is about to render.
4818
+ */
4819
+ GetExportColumns() {
4820
+ const displayed = this.gridApi?.getAllDisplayedColumns() ?? [];
4821
+ const defs = displayed.length > 0
4822
+ ? displayed.map(c => c.getColDef())
4823
+ : this.AgColumnDefs.filter(d => !d.hide);
4824
+ // No `width`: ExportColumn.width is in characters, while grid widths are pixels, so passing them
4825
+ // made a 150px column 150 characters wide. Left unset, the Excel exporter auto-fits each column.
4826
+ return defs
4827
+ .filter(def => this.isExportableColumn(def))
4828
+ .map(def => {
4829
+ const colField = def.field;
4617
4830
  // Case-insensitive, matching how col defs and auto-width resolve a host's field name.
4618
- // An exact-case match here silently cost the export column its data type.
4619
- const field = this._entityInfo?.Fields.find(f => f.Name.toLowerCase() === col.field.toLowerCase());
4831
+ const field = this._entityInfo?.Fields.find(f => f.Name.toLowerCase() === colField.toLowerCase());
4620
4832
  return {
4621
4833
  // The entity's spelling, because `name` is the KEY the export engine reads each row by
4622
4834
  // (`row[col.name]`) and rows are keyed from entity metadata. The host's spelling here
4623
4835
  // exported a correctly-headed column of blank cells.
4624
- name: field?.Name ?? col.field,
4625
- displayName: col.title || field?.DisplayName || col.field,
4626
- dataType: this.mapFieldTypeToExportType(field?.Type),
4627
- width: typeof col.width === 'number' ? col.width : undefined
4836
+ name: field?.Name ?? colField,
4837
+ displayName: def.headerName || field?.DisplayNameOrName || colField,
4838
+ dataType: this.mapFieldTypeToExportType(field?.Type)
4628
4839
  };
4629
4840
  });
4630
4841
  }
4842
+ /** A data column: not the layout filler, not the row-number column, and bound to a field. */
4843
+ isExportableColumn(def) {
4844
+ return !!def.field && def.field !== ROW_NUMBER_FIELD && def.colId !== FILLER_COLUMN_ID;
4845
+ }
4631
4846
  /**
4632
4847
  * Map MemberJunction field types to export column types
4633
4848
  */
@@ -4657,11 +4872,15 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4657
4872
  const timestamp = new Date().toISOString().slice(0, 10);
4658
4873
  return `${entityName}_${timestamp}`;
4659
4874
  }
4660
- onRefreshClick() {
4875
+ OnRefreshClick() {
4661
4876
  this.RefreshButtonClick.emit();
4662
4877
  this.Refresh();
4663
4878
  }
4664
- onCompareClick() {
4879
+ /** @deprecated Use {@link OnRefreshClick}. */
4880
+ onRefreshClick() {
4881
+ return this.OnRefreshClick();
4882
+ }
4883
+ OnCompareClick() {
4665
4884
  const selectedRows = this.GetSelectedRows();
4666
4885
  if (selectedRows.length >= 2) {
4667
4886
  // Emit legacy event for backward compatibility
@@ -4675,7 +4894,11 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4675
4894
  }
4676
4895
  }
4677
4896
  }
4678
- onMergeClick() {
4897
+ /** @deprecated Use {@link OnCompareClick}. */
4898
+ onCompareClick() {
4899
+ return this.OnCompareClick();
4900
+ }
4901
+ OnMergeClick() {
4679
4902
  const selectedRows = this.GetSelectedRows();
4680
4903
  if (selectedRows.length >= 2) {
4681
4904
  // Emit legacy event for backward compatibility
@@ -4689,7 +4912,11 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4689
4912
  }
4690
4913
  }
4691
4914
  }
4692
- async onAddToListClick() {
4915
+ /** @deprecated Use {@link OnMergeClick}. */
4916
+ onMergeClick() {
4917
+ return this.OnMergeClick();
4918
+ }
4919
+ async OnAddToListClick() {
4693
4920
  let rows = this.GetSelectedRows();
4694
4921
  // No explicit selection → add EVERY matching record, not just the loaded page. Without this,
4695
4922
  // building a list from a sizeable view meant selecting-all page by page (bug E3). We fetch the
@@ -4715,7 +4942,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4715
4942
  // Emit new structured event with record IDs for list management
4716
4943
  if (this._entityInfo) {
4717
4944
  const recordIds = rows.map(r => {
4718
- return buildPkString(r, this._entityInfo);
4945
+ return BuildPkString(r, this._entityInfo);
4719
4946
  });
4720
4947
  this.AddToListRequested.emit({
4721
4948
  entityInfo: this._entityInfo,
@@ -4725,7 +4952,11 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4725
4952
  }
4726
4953
  }
4727
4954
  }
4728
- onDuplicateSearchClick() {
4955
+ /** @deprecated Use {@link OnAddToListClick}. */
4956
+ async onAddToListClick() {
4957
+ return this.OnAddToListClick();
4958
+ }
4959
+ OnDuplicateSearchClick() {
4729
4960
  const selectedRows = this.GetSelectedRows();
4730
4961
  if (selectedRows.length >= 2) {
4731
4962
  // Emit legacy event for backward compatibility
@@ -4739,7 +4970,11 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4739
4970
  }
4740
4971
  }
4741
4972
  }
4742
- onCommunicationClick() {
4973
+ /** @deprecated Use {@link OnDuplicateSearchClick}. */
4974
+ onDuplicateSearchClick() {
4975
+ return this.OnDuplicateSearchClick();
4976
+ }
4977
+ OnCommunicationClick() {
4743
4978
  const selectedRows = this.GetSelectedRows();
4744
4979
  if (selectedRows.length > 0) {
4745
4980
  // Emit legacy event for backward compatibility
@@ -4754,6 +4989,10 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4754
4989
  }
4755
4990
  }
4756
4991
  }
4992
+ /** @deprecated Use {@link OnCommunicationClick}. */
4993
+ onCommunicationClick() {
4994
+ return this.OnCommunicationClick();
4995
+ }
4757
4996
  /**
4758
4997
  * "Manage Columns" / column-chooser toolbar affordance. The grid is generic and doesn't own a
4759
4998
  * column-management UI, so it raises {@link ManageColumnsRequested} for its host to handle —
@@ -4761,9 +5000,13 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4761
5000
  * column editor backed by `UserView.GridState`. Hosts that embed the grid standalone can handle
4762
5001
  * this to surface their own column UI.
4763
5002
  */
4764
- onColumnChooserClick() {
5003
+ OnColumnChooserClick() {
4765
5004
  this.ManageColumnsRequested.emit();
4766
5005
  }
5006
+ /** @deprecated Use {@link OnColumnChooserClick}. */
5007
+ onColumnChooserClick() {
5008
+ return this.OnColumnChooserClick();
5009
+ }
4767
5010
  /**
4768
5011
  * The runtime-UX driver currently mounted over the grid (e.g. the Record Process bulk-update runner),
4769
5012
  * or null when none. Set when an action whose invocation names a `RuntimeUXDriverClass` is clicked.
@@ -4774,7 +5017,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4774
5017
  * grid mounts that interactive driver in-place (no parent wiring required); otherwise it emits
4775
5018
  * `EntityActionRequested` for the host to invoke the action the classic way.
4776
5019
  */
4777
- onEntityActionClick(action) {
5020
+ OnEntityActionClick(action) {
4778
5021
  if (!this._entityInfo)
4779
5022
  return;
4780
5023
  if (action.runtimeUXDriverClass) {
@@ -4787,6 +5030,10 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4787
5030
  selectedRecords: this.GetSelectedRows()
4788
5031
  });
4789
5032
  }
5033
+ /** @deprecated Use {@link OnEntityActionClick}. */
5034
+ onEntityActionClick(action) {
5035
+ return this.OnEntityActionClick(action);
5036
+ }
4790
5037
  /** Builds the driver context from the current entity + selection and mounts the named driver. */
4791
5038
  mountRuntimeDriver(action) {
4792
5039
  const entity = this._entityInfo;
@@ -4823,7 +5070,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4823
5070
  /**
4824
5071
  * Checks if an entity action is currently enabled based on selection requirements
4825
5072
  */
4826
- isEntityActionEnabled(action) {
5073
+ IsEntityActionEnabled(action) {
4827
5074
  if (!action.requiresSelection)
4828
5075
  return true;
4829
5076
  const selectedCount = this._selectedKeys.length;
@@ -4835,14 +5082,18 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4835
5082
  return false;
4836
5083
  return true;
4837
5084
  }
5085
+ /** @deprecated Use {@link IsEntityActionEnabled}. */
5086
+ isEntityActionEnabled(action) {
5087
+ return this.IsEntityActionEnabled(action);
5088
+ }
4838
5089
  // ========================================
4839
5090
  // Overflow Menu Methods
4840
5091
  // ========================================
4841
- toggleOverflowMenu() {
4842
- this.showOverflowMenu = !this.showOverflowMenu;
5092
+ ToggleOverflowMenu() {
5093
+ this.ShowOverflowMenu = !this.ShowOverflowMenu;
4843
5094
  this.cdr.detectChanges();
4844
5095
  // Add click outside listener when menu is open
4845
- if (this.showOverflowMenu) {
5096
+ if (this.ShowOverflowMenu) {
4846
5097
  setTimeout(() => {
4847
5098
  document.addEventListener('click', this.handleOutsideClick);
4848
5099
  }, 0);
@@ -4851,34 +5102,62 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4851
5102
  document.removeEventListener('click', this.handleOutsideClick);
4852
5103
  }
4853
5104
  }
4854
- closeOverflowMenu() {
4855
- this.showOverflowMenu = false;
5105
+ /** @deprecated Use {@link ToggleOverflowMenu}. */
5106
+ toggleOverflowMenu() {
5107
+ return this.ToggleOverflowMenu();
5108
+ }
5109
+ CloseOverflowMenu() {
5110
+ this.ShowOverflowMenu = false;
4856
5111
  document.removeEventListener('click', this.handleOutsideClick);
4857
5112
  this.cdr.detectChanges();
4858
5113
  }
5114
+ /** @deprecated Use {@link CloseOverflowMenu}. */
5115
+ closeOverflowMenu() {
5116
+ return this.CloseOverflowMenu();
5117
+ }
4859
5118
  handleOutsideClick = () => {
4860
- this.closeOverflowMenu();
5119
+ this.CloseOverflowMenu();
4861
5120
  };
4862
5121
  // Overflow menu visibility helpers
4863
- get hasOverflowMenuItems() {
4864
- return this.showExportInOverflow ||
4865
- this.showColumnChooserInOverflow ||
5122
+ get HasOverflowMenuItems() {
5123
+ return this.ShowExportInOverflow ||
5124
+ this.ShowColumnChooserInOverflow ||
4866
5125
  (this._showEntityActionButtons && this._entityActions.length > 0) ||
4867
- this.hasSelectionDependentOverflowActions;
5126
+ this.HasSelectionDependentOverflowActions;
4868
5127
  }
4869
- get showExportInOverflow() {
5128
+ /** @deprecated Use {@link HasOverflowMenuItems}. */
5129
+ get hasOverflowMenuItems() {
5130
+ return this.HasOverflowMenuItems;
5131
+ }
5132
+ get ShowExportInOverflow() {
4870
5133
  // Export is in overflow when it's not shown as a primary button
4871
5134
  return !this.ShowExportButton && !!this.ToolbarConfig.showExport;
4872
5135
  }
4873
- get showColumnChooserInOverflow() {
5136
+ /** @deprecated Use {@link ShowExportInOverflow}. */
5137
+ get showExportInOverflow() {
5138
+ return this.ShowExportInOverflow;
5139
+ }
5140
+ get ShowColumnChooserInOverflow() {
4874
5141
  return this.AllowColumnToggle && !this.ToolbarConfig.showColumnChooser;
4875
5142
  }
5143
+ /** @deprecated Use {@link ShowColumnChooserInOverflow}. */
5144
+ get showColumnChooserInOverflow() {
5145
+ return this.ShowColumnChooserInOverflow;
5146
+ }
5147
+ get HasSelectionDependentOverflowActions() {
5148
+ return this.ShowCommunicationInOverflow;
5149
+ }
5150
+ /** @deprecated Use {@link HasSelectionDependentOverflowActions}. */
4876
5151
  get hasSelectionDependentOverflowActions() {
4877
- return this.showCommunicationInOverflow;
5152
+ return this.HasSelectionDependentOverflowActions;
4878
5153
  }
4879
- get showCommunicationInOverflow() {
5154
+ get ShowCommunicationInOverflow() {
4880
5155
  // Communication is in overflow when it's not shown as a primary button
4881
- return !this.ShowCommunicationButton && this.HasSelection;
5156
+ return !this.ShowCommunicationButton && this.HasSelection && this.EntitySupportsCommunication;
5157
+ }
5158
+ /** @deprecated Use {@link ShowCommunicationInOverflow}. */
5159
+ get showCommunicationInOverflow() {
5160
+ return this.ShowCommunicationInOverflow;
4882
5161
  }
4883
5162
  // ========================================
4884
5163
  // Toolbar Button State Helpers
@@ -4892,13 +5171,13 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4892
5171
  // ========================================
4893
5172
  // CSS Helpers
4894
5173
  // ========================================
4895
- get gridContainerClasses() {
5174
+ get GridContainerClasses() {
4896
5175
  const classes = ['mj-grid-container'];
4897
5176
  classes.push(`grid-lines-${this._gridLines}`);
4898
5177
  if (this._striped)
4899
5178
  classes.push('grid-striped');
4900
5179
  // Add visual config classes
4901
- const vc = this.effectiveVisualConfig;
5180
+ const vc = this.EffectiveVisualConfig;
4902
5181
  classes.push(`header-style-${vc.headerStyle}`);
4903
5182
  if (vc.headerShadow)
4904
5183
  classes.push('header-shadow');
@@ -4911,11 +5190,15 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4911
5190
  classes.push(`checkbox-style-${vc.checkboxStyle}`);
4912
5191
  return classes;
4913
5192
  }
5193
+ /** @deprecated Use {@link GridContainerClasses}. */
5194
+ get gridContainerClasses() {
5195
+ return this.GridContainerClasses;
5196
+ }
4914
5197
  /**
4915
5198
  * Apply visual configuration by setting CSS custom properties
4916
5199
  */
4917
5200
  applyVisualConfig() {
4918
- const vc = this.effectiveVisualConfig;
5201
+ const vc = this.EffectiveVisualConfig;
4919
5202
  const el = this.elementRef.nativeElement;
4920
5203
  // Set CSS custom properties for dynamic values
4921
5204
  if (vc.headerBackground) {
@@ -4951,16 +5234,20 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4951
5234
  this.buildAgColumnDefs();
4952
5235
  }
4953
5236
  }
4954
- get gridHeightStyle() {
5237
+ get GridHeightStyle() {
4955
5238
  if (typeof this._height === 'number') {
4956
5239
  return `${this._height}px`;
4957
5240
  }
4958
5241
  return this._height === 'auto' ? '100%' : 'fit-content';
4959
5242
  }
5243
+ /** @deprecated Use {@link GridHeightStyle}. */
5244
+ get gridHeightStyle() {
5245
+ return this.GridHeightStyle;
5246
+ }
4960
5247
  // ========================================
4961
5248
  // Toolbar Button Helpers
4962
5249
  // ========================================
4963
- isButtonVisible(button) {
5250
+ IsButtonVisible(button) {
4964
5251
  if (button.visible === undefined)
4965
5252
  return true;
4966
5253
  if (typeof button.visible === 'boolean')
@@ -4969,7 +5256,11 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4969
5256
  return button.visible();
4970
5257
  return true;
4971
5258
  }
4972
- isButtonDisabled(button) {
5259
+ /** @deprecated Use {@link IsButtonVisible}. */
5260
+ isButtonVisible(button) {
5261
+ return this.IsButtonVisible(button);
5262
+ }
5263
+ IsButtonDisabled(button) {
4973
5264
  if (button.disabled === undefined)
4974
5265
  return false;
4975
5266
  if (typeof button.disabled === 'boolean')
@@ -4978,11 +5269,19 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4978
5269
  return button.disabled();
4979
5270
  return false;
4980
5271
  }
4981
- onToolbarButtonClick(button) {
5272
+ /** @deprecated Use {@link IsButtonDisabled}. */
5273
+ isButtonDisabled(button) {
5274
+ return this.IsButtonDisabled(button);
5275
+ }
5276
+ OnToolbarButtonClick(button) {
4982
5277
  if (button.onClick) {
4983
5278
  button.onClick();
4984
5279
  }
4985
5280
  }
5281
+ /** @deprecated Use {@link OnToolbarButtonClick}. */
5282
+ onToolbarButtonClick(button) {
5283
+ return this.OnToolbarButtonClick(button);
5284
+ }
4986
5285
  // ========================================
4987
5286
  // Public Getters
4988
5287
  // ========================================
@@ -4999,15 +5298,15 @@ export class EntityDataGridComponent extends BaseAngularComponent {
4999
5298
  i0.ɵɵviewQuery(_c0, 5);
5000
5299
  } if (rf & 2) {
5001
5300
  let _t;
5002
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.gridContainer = _t.first);
5003
- } }, inputs: { Params: "Params", AllowLoad: "AllowLoad", AutoRefreshOnParamsChange: "AutoRefreshOnParamsChange", PaginationMode: "PaginationMode", PageSize: "PageSize", CacheBlockSize: "CacheBlockSize", MaxBlocksInCache: "MaxBlocksInCache", ShowPager: "ShowPager", ExportDataProvider: "ExportDataProvider", ShowRecycleBin: "ShowRecycleBin", PagerPageNumber: "PagerPageNumber", TotalRowCount: "TotalRowCount", Data: "Data", Columns: "Columns", GridState: "GridState", AllowColumnReorder: "AllowColumnReorder", AllowColumnResize: "AllowColumnResize", AllowColumnToggle: "AllowColumnToggle", ShowHeader: "ShowHeader", AllowSorting: "AllowSorting", AllowMultiSort: "AllowMultiSort", ServerSideSorting: "ServerSideSorting", AllowColumnFilters: "AllowColumnFilters", ShowSearch: "ShowSearch", SelectionMode: "SelectionMode", SelectedKeys: "SelectedKeys", KeyField: "KeyField", EditMode: "EditMode", AllowAdd: "AllowAdd", AllowDelete: "AllowDelete", Height: "Height", RowHeight: "RowHeight", WrapText: "WrapText", VirtualScroll: "VirtualScroll", ShowRowNumbers: "ShowRowNumbers", FillWidth: "FillWidth", Striped: "Striped", GridLines: "GridLines", VisualConfig: "VisualConfig", ShowToolbar: "ShowToolbar", ToolbarConfig: "ToolbarConfig", StateKey: "StateKey", AutoPersistState: "AutoPersistState", StatePersistDebounce: "StatePersistDebounce", RefreshDebounce: "RefreshDebounce", FilterText: "FilterText", ShowNewButton: "ShowNewButton", ShowRefreshButton: "ShowRefreshButton", ShowExportButton: "ShowExportButton", ShowDeleteButton: "ShowDeleteButton", ShowCompareButton: "ShowCompareButton", ShowMergeButton: "ShowMergeButton", ShowAddToListButton: "ShowAddToListButton", ShowDuplicateSearchButton: "ShowDuplicateSearchButton", ShowCommunicationButton: "ShowCommunicationButton", AutoNavigate: "AutoNavigate", NavigateOnDoubleClick: "NavigateOnDoubleClick", CreateRecordMode: "CreateRecordMode", NewRecordValues: "NewRecordValues", ShowEntityActionButtons: "ShowEntityActionButtons", EntityActions: "EntityActions", AutoLoadEntityActions: "AutoLoadEntityActions", AggregatesConfig: "AggregatesConfig" }, outputs: { PageChange: "PageChange", AggregatesLoaded: "AggregatesLoaded", BeforeRowSelect: "BeforeRowSelect", AfterRowSelect: "AfterRowSelect", BeforeRowDeselect: "BeforeRowDeselect", AfterRowDeselect: "AfterRowDeselect", SelectionChange: "SelectionChange", BeforeRowClick: "BeforeRowClick", AfterRowClick: "AfterRowClick", BeforeRowDoubleClick: "BeforeRowDoubleClick", AfterRowDoubleClick: "AfterRowDoubleClick", ForeignKeyClick: "ForeignKeyClick", BeforeCellEdit: "BeforeCellEdit", AfterCellEditBegin: "AfterCellEditBegin", BeforeCellEditCommit: "BeforeCellEditCommit", AfterCellEditCommit: "AfterCellEditCommit", BeforeCellEditCancel: "BeforeCellEditCancel", AfterCellEditCancel: "AfterCellEditCancel", BeforeRowSave: "BeforeRowSave", AfterRowSave: "AfterRowSave", BeforeRowDelete: "BeforeRowDelete", AfterRowDelete: "AfterRowDelete", BeforeDataLoad: "BeforeDataLoad", AfterDataLoad: "AfterDataLoad", BeforeDataRefresh: "BeforeDataRefresh", AfterDataRefresh: "AfterDataRefresh", BeforeSort: "BeforeSort", AfterSort: "AfterSort", BeforeColumnReorder: "BeforeColumnReorder", AfterColumnReorder: "AfterColumnReorder", BeforeColumnResize: "BeforeColumnResize", AfterColumnResize: "AfterColumnResize", BeforeColumnVisibilityChange: "BeforeColumnVisibilityChange", AfterColumnVisibilityChange: "AfterColumnVisibilityChange", GridStateChanged: "GridStateChanged", AddRequested: "AddRequested", DeleteRequested: "DeleteRequested", ExportRequested: "ExportRequested", NewButtonClick: "NewButtonClick", RefreshButtonClick: "RefreshButtonClick", ExportButtonClick: "ExportButtonClick", DeleteButtonClick: "DeleteButtonClick", CompareButtonClick: "CompareButtonClick", MergeButtonClick: "MergeButtonClick", AddToListButtonClick: "AddToListButtonClick", DuplicateSearchButtonClick: "DuplicateSearchButtonClick", CommunicationButtonClick: "CommunicationButtonClick", ManageColumnsRequested: "ManageColumnsRequested", NavigationRequested: "NavigationRequested", NewRecordDialogRequested: "NewRecordDialogRequested", NewRecordTabRequested: "NewRecordTabRequested", CompareRecordsRequested: "CompareRecordsRequested", MergeRecordsRequested: "MergeRecordsRequested", CommunicationRequested: "CommunicationRequested", DuplicateSearchRequested: "DuplicateSearchRequested", AddToListRequested: "AddToListRequested", LoadEntityActionsRequested: "LoadEntityActionsRequested", EntityActionRequested: "EntityActionRequested" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 14, vars: 16, consts: [["gridContainer", ""], [1, "mj-grid-toolbar"], [1, "mj-aggregate-cards"], [1, "mj-grid-content"], [1, "mj-grid-loading-overlay"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Couldn't load this data", "ActionText", "Retry", "ActionIcon", "fa-solid fa-arrows-rotate", 1, "mj-grid-state-fill", 3, "Size", "Message"], ["Icon", "fa-solid fa-inbox", "Title", "No data to display", 1, "mj-grid-state-fill", 3, "Size"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "theme", "columnDefs", "rowData", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "theme", "columnDefs", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight", "rowModelType", "cacheBlockSize", "maxBlocksInCache", "infiniteInitialRowCount", "cacheOverflowSize"], [3, "TotalRowCount", "PageNumber", "PageSize", "IsLoading"], [1, "mj-aggregate-summary"], [3, "closed", "visible", "config"], [3, "DriverClass", "Context"], [1, "toolbar-left"], [1, "toolbar-search"], [1, "toolbar-center"], [1, "row-count"], [1, "selection-count"], [1, "toolbar-right"], [3, "EntityName"], ["title", "Create new record", 1, "toolbar-button"], ["title", "Refresh data", 1, "toolbar-button", 3, "disabled"], ["title", "Export", 1, "toolbar-button", 3, "disabled"], ["title", "Delete selected records", 1, "toolbar-button", "toolbar-button-danger"], ["title", "Compare selected records", 1, "toolbar-button", 3, "disabled"], ["title", "Merge selected records", 1, "toolbar-button", 3, "disabled"], [1, "toolbar-button", 3, "title", "disabled"], ["title", "Search for duplicate records", 1, "toolbar-button", 3, "disabled"], ["title", "Send message to selected records", 1, "toolbar-button", 3, "disabled"], ["title", "Add New", 1, "toolbar-button"], ["title", "Refresh", 1, "toolbar-button", 3, "disabled"], ["title", "Delete Selected", 1, "toolbar-button", "toolbar-button-danger"], ["title", "Export", 1, "toolbar-button"], ["title", "Column Chooser", 1, "toolbar-button"], [1, "toolbar-overflow"], [1, "fa-solid", "fa-search", "search-icon"], ["type", "text", 1, "search-input", 3, "input", "placeholder", "value"], ["aria-label", "Clear search", 1, "search-clear"], ["aria-label", "Clear search", 1, "search-clear", 3, "click"], [1, "fa-solid", "fa-times"], [1, "toolbar-button", 3, "class", "disabled", "title"], [1, "toolbar-button", 3, "click", "disabled", "title"], [3, "class"], ["title", "Create new record", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "button-text"], ["title", "Refresh data", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-arrows-rotate"], ["title", "Export", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid"], ["title", "Delete selected records", 1, "toolbar-button", "toolbar-button-danger", 3, "click"], [1, "fa-solid", "fa-trash"], ["title", "Compare selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-code-compare"], ["title", "Merge selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-code-merge"], [1, "toolbar-button", 3, "click", "title", "disabled"], ["title", "Search for duplicate records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-magnifying-glass-plus"], ["title", "Send message to selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-envelope"], ["title", "Add New", 1, "toolbar-button", 3, "click"], ["title", "Refresh", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-refresh"], ["title", "Delete Selected", 1, "toolbar-button", "toolbar-button-danger", 3, "click"], ["title", "Export", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-download"], ["title", "Column Chooser", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-columns"], [1, "toolbar-overflow", 3, "click"], ["title", "More actions", 1, "toolbar-button", "overflow-trigger", 3, "click"], [1, "fa-solid", "fa-ellipsis-vertical"], [1, "overflow-menu"], [1, "overflow-item"], [1, "overflow-divider"], [1, "overflow-item", 3, "click"], [1, "fa-solid", "fa-file-excel"], [1, "overflow-section-label"], [1, "overflow-item", 3, "disabled"], [1, "overflow-item", 3, "click", "disabled"], [1, "aggregate-card"], [1, "aggregate-card-loading"], [1, "aggregate-card-icon"], [1, "aggregate-card-content"], [1, "aggregate-card-label"], [1, "aggregate-card-value"], [1, "fa-solid", "fa-spinner", "fa-spin"], ["text", "Loading..."], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Couldn't load this data", "ActionText", "Retry", "ActionIcon", "fa-solid fa-arrows-rotate", 1, "mj-grid-state-fill", 3, "Action", "Size", "Message"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "gridReady", "cellClicked", "rowClicked", "rowDoubleClicked", "sortChanged", "selectionChanged", "columnResized", "columnMoved", "theme", "columnDefs", "rowData", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "gridReady", "cellClicked", "rowClicked", "rowDoubleClicked", "sortChanged", "selectionChanged", "columnResized", "columnMoved", "theme", "columnDefs", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight", "rowModelType", "cacheBlockSize", "maxBlocksInCache", "infiniteInitialRowCount", "cacheOverflowSize"], [3, "PageChange", "TotalRowCount", "PageNumber", "PageSize", "IsLoading"], [1, "aggregate-summary-content"], [1, "aggregate-summary-item"], [1, "aggregate-loading"], [1, "agg-summary-label"], [1, "agg-summary-value"], [3, "Completed", "Cancelled", "DriverClass", "Context"]], template: function EntityDataGridComponent_Template(rf, ctx) { if (rf & 1) {
5301
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.GridContainer = _t.first);
5302
+ } }, inputs: { Params: "Params", AllowLoad: "AllowLoad", AutoRefreshOnParamsChange: "AutoRefreshOnParamsChange", PaginationMode: "PaginationMode", PageSize: "PageSize", CacheBlockSize: "CacheBlockSize", MaxBlocksInCache: "MaxBlocksInCache", ShowPager: "ShowPager", ExportDataProvider: "ExportDataProvider", ShowRecycleBin: "ShowRecycleBin", PagerPageNumber: "PagerPageNumber", TotalRowCount: "TotalRowCount", Data: "Data", Columns: "Columns", GridState: "GridState", AllowColumnReorder: "AllowColumnReorder", AllowColumnResize: "AllowColumnResize", AllowColumnToggle: "AllowColumnToggle", ShowHeader: "ShowHeader", AllowSorting: "AllowSorting", AllowMultiSort: "AllowMultiSort", ServerSideSorting: "ServerSideSorting", AllowColumnFilters: "AllowColumnFilters", ShowSearch: "ShowSearch", SelectionMode: "SelectionMode", SelectedKeys: "SelectedKeys", KeyField: "KeyField", EditMode: "EditMode", AllowAdd: "AllowAdd", AllowDelete: "AllowDelete", Height: "Height", RowHeight: "RowHeight", WrapText: "WrapText", VirtualScroll: "VirtualScroll", ShowRowNumbers: "ShowRowNumbers", FillWidth: "FillWidth", Striped: "Striped", GridLines: "GridLines", VisualConfig: "VisualConfig", ShowToolbar: "ShowToolbar", ToolbarConfig: "ToolbarConfig", StateKey: "StateKey", AutoPersistState: "AutoPersistState", StatePersistDebounce: "StatePersistDebounce", RefreshDebounce: "RefreshDebounce", FilterText: "FilterText", ShowNewButton: "ShowNewButton", ShowRefreshButton: "ShowRefreshButton", ShowExportButton: "ShowExportButton", ShowDeleteButton: "ShowDeleteButton", ShowCompareButton: "ShowCompareButton", ShowMergeButton: "ShowMergeButton", ShowAddToListButton: "ShowAddToListButton", ShowDuplicateSearchButton: "ShowDuplicateSearchButton", ShowCommunicationButton: "ShowCommunicationButton", AutoNavigate: "AutoNavigate", NavigateOnDoubleClick: "NavigateOnDoubleClick", CreateRecordMode: "CreateRecordMode", NewRecordValues: "NewRecordValues", ShowEntityActionButtons: "ShowEntityActionButtons", EntityActions: "EntityActions", AutoLoadEntityActions: "AutoLoadEntityActions", AggregatesConfig: "AggregatesConfig" }, outputs: { PageChange: "PageChange", AggregatesLoaded: "AggregatesLoaded", BeforeRowSelect: "BeforeRowSelect", AfterRowSelect: "AfterRowSelect", BeforeRowDeselect: "BeforeRowDeselect", AfterRowDeselect: "AfterRowDeselect", SelectionChange: "SelectionChange", BeforeRowClick: "BeforeRowClick", AfterRowClick: "AfterRowClick", BeforeRowDoubleClick: "BeforeRowDoubleClick", AfterRowDoubleClick: "AfterRowDoubleClick", ForeignKeyClick: "ForeignKeyClick", BeforeCellEdit: "BeforeCellEdit", AfterCellEditBegin: "AfterCellEditBegin", BeforeCellEditCommit: "BeforeCellEditCommit", AfterCellEditCommit: "AfterCellEditCommit", BeforeCellEditCancel: "BeforeCellEditCancel", AfterCellEditCancel: "AfterCellEditCancel", BeforeRowSave: "BeforeRowSave", AfterRowSave: "AfterRowSave", BeforeRowDelete: "BeforeRowDelete", AfterRowDelete: "AfterRowDelete", BeforeDataLoad: "BeforeDataLoad", AfterDataLoad: "AfterDataLoad", BeforeDataRefresh: "BeforeDataRefresh", AfterDataRefresh: "AfterDataRefresh", BeforeSort: "BeforeSort", AfterSort: "AfterSort", BeforeColumnReorder: "BeforeColumnReorder", AfterColumnReorder: "AfterColumnReorder", BeforeColumnResize: "BeforeColumnResize", AfterColumnResize: "AfterColumnResize", BeforeColumnVisibilityChange: "BeforeColumnVisibilityChange", AfterColumnVisibilityChange: "AfterColumnVisibilityChange", GridStateChanged: "GridStateChanged", AddRequested: "AddRequested", DeleteRequested: "DeleteRequested", ExportRequested: "ExportRequested", NewButtonClick: "NewButtonClick", RefreshButtonClick: "RefreshButtonClick", ExportButtonClick: "ExportButtonClick", DeleteButtonClick: "DeleteButtonClick", CompareButtonClick: "CompareButtonClick", MergeButtonClick: "MergeButtonClick", AddToListButtonClick: "AddToListButtonClick", DuplicateSearchButtonClick: "DuplicateSearchButtonClick", CommunicationButtonClick: "CommunicationButtonClick", ManageColumnsRequested: "ManageColumnsRequested", NavigationRequested: "NavigationRequested", NewRecordDialogRequested: "NewRecordDialogRequested", NewRecordTabRequested: "NewRecordTabRequested", CompareRecordsRequested: "CompareRecordsRequested", MergeRecordsRequested: "MergeRecordsRequested", CommunicationRequested: "CommunicationRequested", DuplicateSearchRequested: "DuplicateSearchRequested", AddToListRequested: "AddToListRequested", LoadEntityActionsRequested: "LoadEntityActionsRequested", EntityActionRequested: "EntityActionRequested" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 14, vars: 16, consts: [["gridContainer", ""], [1, "mj-grid-toolbar"], [1, "mj-aggregate-cards"], [1, "mj-grid-content"], [1, "mj-grid-loading-overlay"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Couldn't load this data", "ActionText", "Retry", "ActionIcon", "fa-solid fa-arrows-rotate", 1, "mj-grid-state-fill", 3, "Size", "Message"], ["Icon", "fa-solid fa-inbox", 1, "mj-grid-state-fill", 3, "Size", "Title"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "theme", "columnDefs", "rowData", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "theme", "columnDefs", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight", "rowModelType", "cacheBlockSize", "maxBlocksInCache", "infiniteInitialRowCount", "cacheOverflowSize"], [3, "TotalRowCount", "PageNumber", "PageSize", "IsLoading"], [1, "mj-aggregate-summary"], [3, "closed", "visible", "config"], [3, "DriverClass", "Context"], [1, "toolbar-left"], [1, "toolbar-search"], [1, "toolbar-center"], [1, "row-count"], [1, "selection-count"], [1, "toolbar-right"], [3, "EntityName"], ["title", "Create new record", 1, "toolbar-button"], ["title", "Refresh data", 1, "toolbar-button", 3, "disabled"], ["title", "Export", 1, "toolbar-button", 3, "disabled"], ["title", "Delete selected records", 1, "toolbar-button", "toolbar-button-danger"], ["title", "Compare selected records", 1, "toolbar-button", 3, "disabled"], ["title", "Merge selected records", 1, "toolbar-button", 3, "disabled"], [1, "toolbar-button", 3, "title", "disabled"], ["title", "Search for duplicate records", 1, "toolbar-button", 3, "disabled"], ["title", "Send message to selected records", 1, "toolbar-button", 3, "disabled"], ["title", "Add New", 1, "toolbar-button"], ["title", "Refresh", 1, "toolbar-button", 3, "disabled"], ["title", "Delete Selected", 1, "toolbar-button", "toolbar-button-danger"], ["title", "Export", 1, "toolbar-button"], ["title", "Column Chooser", 1, "toolbar-button"], [1, "toolbar-overflow"], [1, "fa-solid", "fa-search", "search-icon"], ["type", "text", 1, "search-input", 3, "input", "placeholder", "value"], ["aria-label", "Clear search", 1, "search-clear"], ["aria-label", "Clear search", 1, "search-clear", 3, "click"], [1, "fa-solid", "fa-times"], [1, "toolbar-button", 3, "class", "disabled", "title"], [1, "toolbar-button", 3, "click", "disabled", "title"], [3, "class"], ["title", "Create new record", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "button-text"], ["title", "Refresh data", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-arrows-rotate"], ["title", "Export", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid"], ["title", "Delete selected records", 1, "toolbar-button", "toolbar-button-danger", 3, "click"], [1, "fa-solid", "fa-trash"], ["title", "Compare selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-code-compare"], ["title", "Merge selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-code-merge"], [1, "toolbar-button", 3, "click", "title", "disabled"], ["title", "Search for duplicate records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-magnifying-glass-plus"], ["title", "Send message to selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-envelope"], ["title", "Add New", 1, "toolbar-button", 3, "click"], ["title", "Refresh", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-refresh"], ["title", "Delete Selected", 1, "toolbar-button", "toolbar-button-danger", 3, "click"], ["title", "Export", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-download"], ["title", "Column Chooser", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-columns"], [1, "toolbar-overflow", 3, "click"], ["title", "More actions", 1, "toolbar-button", "overflow-trigger", 3, "click"], [1, "fa-solid", "fa-ellipsis-vertical"], [1, "overflow-menu"], [1, "overflow-item"], [1, "overflow-divider"], [1, "overflow-item", 3, "click"], [1, "fa-solid", "fa-file-excel"], [1, "overflow-section-label"], [1, "overflow-item", 3, "disabled"], [1, "overflow-item", 3, "click", "disabled"], [1, "aggregate-card"], [1, "aggregate-card-loading"], [1, "aggregate-card-icon"], [1, "aggregate-card-content"], [1, "aggregate-card-label"], [1, "aggregate-card-value"], [1, "fa-solid", "fa-spinner", "fa-spin"], ["text", "Loading..."], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Couldn't load this data", "ActionText", "Retry", "ActionIcon", "fa-solid fa-arrows-rotate", 1, "mj-grid-state-fill", 3, "Action", "Size", "Message"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "gridReady", "cellClicked", "rowClicked", "rowDoubleClicked", "sortChanged", "selectionChanged", "columnResized", "columnMoved", "theme", "columnDefs", "rowData", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "gridReady", "cellClicked", "rowClicked", "rowDoubleClicked", "sortChanged", "selectionChanged", "columnResized", "columnMoved", "theme", "columnDefs", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight", "rowModelType", "cacheBlockSize", "maxBlocksInCache", "infiniteInitialRowCount", "cacheOverflowSize"], [3, "PageChange", "TotalRowCount", "PageNumber", "PageSize", "IsLoading"], [1, "aggregate-summary-content"], [1, "aggregate-summary-item"], [1, "aggregate-loading"], [1, "agg-summary-label"], [1, "agg-summary-value"], [3, "Completed", "Cancelled", "DriverClass", "Context"]], template: function EntityDataGridComponent_Template(rf, ctx) { if (rf & 1) {
5004
5303
  i0.ɵɵelementStart(0, "div", null, 0);
5005
5304
  i0.ɵɵconditionalCreate(2, EntityDataGridComponent_Conditional_2_Template, 27, 19, "div", 1);
5006
5305
  i0.ɵɵconditionalCreate(3, EntityDataGridComponent_Conditional_3_Template, 4, 1, "div", 2);
5007
5306
  i0.ɵɵelementStart(4, "div", 3);
5008
5307
  i0.ɵɵconditionalCreate(5, EntityDataGridComponent_Conditional_5_Template, 2, 0, "div", 4);
5009
5308
  i0.ɵɵconditionalCreate(6, EntityDataGridComponent_Conditional_6_Template, 1, 2, "mj-empty-state", 5);
5010
- i0.ɵɵconditionalCreate(7, EntityDataGridComponent_Conditional_7_Template, 1, 1, "mj-empty-state", 6);
5309
+ i0.ɵɵconditionalCreate(7, EntityDataGridComponent_Conditional_7_Template, 1, 2, "mj-empty-state", 6);
5011
5310
  i0.ɵɵconditionalCreate(8, EntityDataGridComponent_Conditional_8_Template, 1, 9, "ag-grid-angular", 7);
5012
5311
  i0.ɵɵconditionalCreate(9, EntityDataGridComponent_Conditional_9_Template, 1, 13, "ag-grid-angular", 8);
5013
5312
  i0.ɵɵelementEnd();
@@ -5067,7 +5366,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
5067
5366
  animate('100ms ease-in', style({ opacity: 0, transform: 'translateY(-8px)' }))
5068
5367
  ])
5069
5368
  ])
5070
- ], template: "<!-- Grid Container -->\n<div\n #gridContainer\n [class]=\"gridContainerClasses.join(' ')\"\n [style.height]=\"gridHeightStyle\">\n\n <!-- Toolbar -->\n @if (ShowToolbar) {\n <div class=\"mj-grid-toolbar\">\n <div class=\"toolbar-left\">\n <!-- Search -->\n @if (ShowSearch) {\n <div class=\"toolbar-search\">\n <i class=\"fa-solid fa-search search-icon\"></i>\n <input\n type=\"text\"\n class=\"search-input\"\n [placeholder]=\"ToolbarConfig.searchPlaceholder || 'Search...'\"\n [value]=\"FilterText\"\n (input)=\"FilterText = $any($event.target).value\" />\n @if (FilterText) {\n <button\n class=\"search-clear\"\n (click)=\"FilterText = ''\"\n aria-label=\"Clear search\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n }\n <!-- Custom Left Buttons -->\n @for (button of ToolbarConfig.customButtons; track button) {\n @if (button.position !== 'right' && isButtonVisible(button)) {\n <button\n class=\"toolbar-button\"\n [class]=\"button.cssClass\"\n [disabled]=\"isButtonDisabled(button)\"\n [title]=\"button.tooltip || ''\"\n (click)=\"onToolbarButtonClick(button)\">\n @if (button.icon) {\n <i [class]=\"button.icon\"></i>\n }\n @if (button.text) {\n <span>{{ button.text }}</span>\n }\n </button>\n }\n }\n </div>\n <div class=\"toolbar-center\">\n <!-- Row Count -->\n @if (ToolbarConfig.showRowCount !== false) {\n <span class=\"row-count\">\n {{ totalRowCount }} {{ totalRowCount === 1 ? 'row' : 'rows' }}\n </span>\n }\n <!-- Selection Count -->\n @if (ToolbarConfig.showSelectionCount && SelectedKeys.length > 0) {\n <span class=\"selection-count\">\n ({{ SelectedKeys.length }} selected)\n </span>\n }\n </div>\n <div class=\"toolbar-right\">\n <!-- Recycle Bin chip \u2014 auto-hides when no deleted records, no tracking,\n or user lacks Delete permission. -->\n @if (ShowRecycleBin) {\n <mj-recycle-bin-chip [EntityName]=\"entityInfoName\"></mj-recycle-bin-chip>\n }\n <!-- New/Add Button (predefined) -->\n @if (ShowNewButton) {\n <button\n class=\"toolbar-button\"\n title=\"Create new record\"\n (click)=\"onAddClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n <span class=\"button-text\">New</span>\n </button>\n }\n <!-- Refresh Button (predefined) -->\n @if (ShowRefreshButton) {\n <button\n class=\"toolbar-button\"\n title=\"Refresh data\"\n [disabled]=\"loading\"\n (click)=\"onRefreshClick()\">\n <i class=\"fa-solid fa-arrows-rotate\" [class.fa-spin]=\"loading\"></i>\n <span class=\"button-text\">Refresh</span>\n </button>\n }\n <!-- Export Button (predefined) -->\n @if (ShowExportButton) {\n <button\n class=\"toolbar-button\"\n title=\"Export\"\n [disabled]=\"IsPreparingExport\"\n (click)=\"onExportClick()\">\n <i class=\"fa-solid\" [class.fa-file-excel]=\"!IsPreparingExport\" [class.fa-spinner]=\"IsPreparingExport\" [class.fa-spin]=\"IsPreparingExport\"></i>\n <span class=\"button-text\">Export</span>\n </button>\n }\n <!-- Delete Button (predefined) -->\n @if (ShowDeleteButton && HasSelection) {\n <button\n class=\"toolbar-button toolbar-button-danger\"\n title=\"Delete selected records\"\n (click)=\"onDeleteClick()\">\n <i class=\"fa-solid fa-trash\"></i>\n <span class=\"button-text\">Delete</span>\n </button>\n }\n <!-- Compare Button (predefined) -->\n @if (ShowCompareButton) {\n <button\n class=\"toolbar-button\"\n title=\"Compare selected records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onCompareClick()\">\n <i class=\"fa-solid fa-code-compare\"></i>\n <span class=\"button-text\">Compare</span>\n </button>\n }\n <!-- Merge Button (predefined) -->\n @if (ShowMergeButton) {\n <button\n class=\"toolbar-button\"\n title=\"Merge selected records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onMergeClick()\">\n <i class=\"fa-solid fa-code-merge\"></i>\n <span class=\"button-text\">Merge</span>\n </button>\n }\n <!-- Add to List Button (predefined) -->\n @if (ShowAddToListButton) {\n <button\n class=\"toolbar-button\"\n [title]=\"HasSelection ? 'Add selected records to a list' : 'Add all matching records to a list'\"\n [disabled]=\"(!HasSelection && !ExportDataProvider) || IsPreparingAddToList\"\n (click)=\"onAddToListClick()\">\n <i class=\"fa-solid\" [class.fa-list-check]=\"!IsPreparingAddToList\" [class.fa-spinner]=\"IsPreparingAddToList\" [class.fa-spin]=\"IsPreparingAddToList\"></i>\n <span class=\"button-text\">Add to List</span>\n </button>\n }\n <!-- Search for Duplicates Button (predefined) -->\n @if (ShowDuplicateSearchButton) {\n <button\n class=\"toolbar-button\"\n title=\"Search for duplicate records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onDuplicateSearchClick()\">\n <i class=\"fa-solid fa-magnifying-glass-plus\"></i>\n <span class=\"button-text\">Find Duplicates</span>\n </button>\n }\n <!-- Communication Button (predefined) -->\n @if (ShowCommunicationButton) {\n <button\n class=\"toolbar-button\"\n title=\"Send message to selected records\"\n [disabled]=\"!HasSelection\"\n (click)=\"onCommunicationClick()\">\n <i class=\"fa-solid fa-envelope\"></i>\n <span class=\"button-text\">Send Message</span>\n </button>\n }\n <!-- Legacy ToolbarConfig buttons -->\n <!-- Add Button (legacy) -->\n @if (ToolbarConfig.showAdd && AllowAdd && !ShowNewButton) {\n <button\n class=\"toolbar-button\"\n title=\"Add New\"\n (click)=\"onAddClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n }\n <!-- Refresh Button (legacy) -->\n @if (ToolbarConfig.showRefresh !== false && !ShowRefreshButton) {\n <button\n class=\"toolbar-button\"\n title=\"Refresh\"\n [disabled]=\"loading\"\n (click)=\"onRefreshClick()\">\n <i class=\"fa-solid fa-refresh\" [class.fa-spin]=\"loading\"></i>\n </button>\n }\n <!-- Delete Button (legacy) -->\n @if (ToolbarConfig.showDelete && AllowDelete && HasSelection && !ShowDeleteButton) {\n <button\n class=\"toolbar-button toolbar-button-danger\"\n title=\"Delete Selected\"\n (click)=\"onDeleteClick()\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n }\n <!-- Export Button (legacy) -->\n @if (ToolbarConfig.showExport && !ShowExportButton) {\n <button\n class=\"toolbar-button\"\n title=\"Export\"\n (click)=\"onExportClick()\">\n <i class=\"fa-solid fa-download\"></i>\n </button>\n }\n <!-- Column Chooser Button -->\n @if (ToolbarConfig.showColumnChooser && AllowColumnToggle) {\n <button\n class=\"toolbar-button\"\n title=\"Column Chooser\"\n (click)=\"onColumnChooserClick()\">\n <i class=\"fa-solid fa-columns\"></i>\n </button>\n }\n <!-- Custom Right Buttons -->\n @for (button of ToolbarConfig.customButtons; track button) {\n @if (button.position === 'right' && isButtonVisible(button)) {\n <button\n class=\"toolbar-button\"\n [class]=\"button.cssClass\"\n [disabled]=\"isButtonDisabled(button)\"\n [title]=\"button.tooltip || ''\"\n (click)=\"onToolbarButtonClick(button)\">\n @if (button.icon) {\n <i [class]=\"button.icon\"></i>\n }\n @if (button.text) {\n <span>{{ button.text }}</span>\n }\n </button>\n }\n }\n <!-- Overflow Menu -->\n @if (hasOverflowMenuItems) {\n <div class=\"toolbar-overflow\" (click)=\"$event.stopPropagation()\">\n <button\n class=\"toolbar-button overflow-trigger\"\n title=\"More actions\"\n (click)=\"toggleOverflowMenu()\">\n <i class=\"fa-solid fa-ellipsis-vertical\"></i>\n </button>\n @if (showOverflowMenu) {\n <div class=\"overflow-menu\" [@fadeIn]>\n <!-- Export (if in overflow) -->\n @if (showExportInOverflow) {\n <button class=\"overflow-item\" (click)=\"onExportClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-file-excel\"></i>\n <span>Export</span>\n </button>\n }\n <!-- Entity Actions -->\n @if (ShowEntityActionButtons && EntityActions.length > 0) {\n <div class=\"overflow-divider\"></div>\n <div class=\"overflow-section-label\">Actions</div>\n @for (action of EntityActions; track action) {\n <button\n class=\"overflow-item\"\n [disabled]=\"!isEntityActionEnabled(action)\"\n (click)=\"onEntityActionClick(action); closeOverflowMenu()\">\n <i [class]=\"action.icon || 'fa-solid fa-bolt'\"></i>\n <span>{{ action.name }}</span>\n </button>\n }\n }\n <!-- Column Chooser (if in overflow) -->\n @if (showColumnChooserInOverflow) {\n <div class=\"overflow-divider\"></div>\n }\n @if (showColumnChooserInOverflow) {\n <button class=\"overflow-item\" (click)=\"onColumnChooserClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-columns\"></i>\n <span>Manage Columns</span>\n </button>\n }\n <!-- Selection-dependent actions in overflow -->\n @if (HasSelection && hasSelectionDependentOverflowActions) {\n <div class=\"overflow-divider\"></div>\n @if (showCommunicationInOverflow) {\n <button class=\"overflow-item\" (click)=\"onCommunicationClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-envelope\"></i>\n <span>Send Message</span>\n </button>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Aggregate Cards (displayed as summary cards above the grid) -->\n @if (ShowAggregatePanel) {\n <div class=\"mj-aggregate-cards\">\n @for (agg of CardAggregates; track agg) {\n <div class=\"aggregate-card\">\n @if (agg.icon) {\n <div class=\"aggregate-card-icon\">\n <i [class]=\"agg.icon\"></i>\n </div>\n }\n <div class=\"aggregate-card-content\">\n <span class=\"aggregate-card-label\">{{ agg.label }}</span>\n <span class=\"aggregate-card-value\">{{ getAggregateValue(agg) }}</span>\n </div>\n </div>\n }\n @if (AggregatesLoading) {\n <div class=\"aggregate-card-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </div>\n }\n </div>\n }\n\n <!-- Grid Content -->\n <div class=\"mj-grid-content\">\n <!-- Loading Overlay -->\n @if (loading && rowData.length === 0) {\n <div class=\"mj-grid-loading-overlay\">\n <mj-loading text=\"Loading...\"></mj-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (errorMessage && !loading) {\n <mj-empty-state\n class=\"mj-grid-state-fill\"\n [Size]=\"EmptyStateSize\"\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n Title=\"Couldn't load this data\"\n [Message]=\"FriendlyErrorMessage\"\n ActionText=\"Retry\"\n ActionIcon=\"fa-solid fa-arrows-rotate\"\n (Action)=\"Refresh()\" />\n }\n\n <!-- Empty State -->\n @if (!loading && !errorMessage && rowData.length === 0) {\n <mj-empty-state\n class=\"mj-grid-state-fill\"\n [Size]=\"EmptyStateSize\"\n Icon=\"fa-solid fa-inbox\"\n Title=\"No data to display\" />\n }\n\n <!-- AG Grid (Client-side mode) -->\n @if (!errorMessage && PaginationMode === 'client' && (rowData.length > 0 || loading)) {\n <ag-grid-angular\n class=\"mj-ag-grid ag-theme-alpine\"\n [theme]=\"agGridTheme\"\n [columnDefs]=\"agColumnDefs\"\n [rowData]=\"rowData\"\n [defaultColDef]=\"defaultColDef\"\n [rowSelection]=\"agRowSelection\"\n [getRowId]=\"getRowId\"\n [suppressCellFocus]=\"true\"\n [rowHeight]=\"RowHeight\"\n [headerHeight]=\"ShowHeader ? undefined : 0\"\n (gridReady)=\"onGridReady($event)\"\n (cellClicked)=\"onAgCellClicked($event)\"\n (rowClicked)=\"onAgRowClicked($event)\"\n (rowDoubleClicked)=\"onAgRowDoubleClicked($event)\"\n (sortChanged)=\"onAgSortChanged($event)\"\n (selectionChanged)=\"onAgSelectionChanged($event)\"\n (columnResized)=\"onAgColumnResized($event)\"\n (columnMoved)=\"onAgColumnMoved($event)\">\n </ag-grid-angular>\n }\n\n <!-- AG Grid (Infinite Scroll mode) -->\n @if (!errorMessage && PaginationMode === 'infinite') {\n <ag-grid-angular\n class=\"mj-ag-grid ag-theme-alpine\"\n [theme]=\"agGridTheme\"\n [columnDefs]=\"agColumnDefs\"\n [defaultColDef]=\"defaultColDef\"\n [rowSelection]=\"agRowSelection\"\n [getRowId]=\"getRowId\"\n [suppressCellFocus]=\"true\"\n [rowHeight]=\"RowHeight\"\n [headerHeight]=\"ShowHeader ? undefined : 0\"\n [rowModelType]=\"'infinite'\"\n [cacheBlockSize]=\"CacheBlockSize\"\n [maxBlocksInCache]=\"MaxBlocksInCache\"\n [infiniteInitialRowCount]=\"1\"\n [cacheOverflowSize]=\"2\"\n (gridReady)=\"onGridReady($event)\"\n (cellClicked)=\"onAgCellClicked($event)\"\n (rowClicked)=\"onAgRowClicked($event)\"\n (rowDoubleClicked)=\"onAgRowDoubleClicked($event)\"\n (sortChanged)=\"onAgSortChanged($event)\"\n (selectionChanged)=\"onAgSelectionChanged($event)\"\n (columnResized)=\"onAgColumnResized($event)\"\n (columnMoved)=\"onAgColumnMoved($event)\">\n </ag-grid-angular>\n }\n\n </div>\n\n <!-- Pagination (opt-in via ShowPager input) -->\n @if (ShowPager) {\n <mj-pagination\n [TotalRowCount]=\"totalRowCount\"\n [PageNumber]=\"PagerPageNumber\"\n [PageSize]=\"PageSize\"\n [IsLoading]=\"loading\"\n (PageChange)=\"PageChange.emit($event)\">\n </mj-pagination>\n }\n\n <!-- Aggregate Summary Row (outside mj-grid-content so it's always visible) -->\n @if (ShowAggregateSummary) {\n <div class=\"mj-aggregate-summary\">\n <div class=\"aggregate-summary-content\">\n @for (agg of ColumnAggregates; track agg) {\n <div class=\"aggregate-summary-item\">\n @if (agg.icon) {\n <i [class]=\"agg.icon\"></i>\n }\n <span class=\"agg-summary-label\">{{ agg.label }}:</span>\n <span class=\"agg-summary-value\">{{ getAggregateValue(agg) }}</span>\n </div>\n }\n </div>\n @if (AggregatesLoading) {\n <div class=\"aggregate-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </div>\n }\n </div>\n }\n</div>\n\n<!-- Export Dialog -->\n<mj-export-dialog\n [visible]=\"showExportDialog\"\n [config]=\"exportDialogConfig\"\n (closed)=\"onExportDialogClosed($event)\">\n</mj-export-dialog>\n\n<!-- Runtime-UX driver for an entity action that names a RuntimeUXDriverClass (e.g. the Record Process\n bulk-update runner). Mounted in-place over the grid \u2014 no parent wiring required. -->\n@if (ActiveRuntimeDriver) {\n <mj-entity-action-ux-host\n [DriverClass]=\"ActiveRuntimeDriver.DriverClass\"\n [Context]=\"ActiveRuntimeDriver.Context\"\n (Completed)=\"OnRuntimeDriverCompleted($event)\"\n (Cancelled)=\"OnRuntimeDriverCancelled()\">\n </mj-entity-action-ux-host>\n}\n", styles: ["/* ========================================\n CSS Custom Properties (Theme Variables)\n ======================================== */\n\n:host {\n /* Grid container */\n --grid-border-color: var(--mj-border-default);\n --grid-border-radius: 0px;\n --grid-background: var(--mj-bg-surface);\n\n /* Header */\n --grid-header-bg: var(--mj-bg-surface-card);\n --grid-header-text: var(--mj-text-primary);\n --grid-header-font-weight: 600;\n --grid-header-height: 40px;\n --grid-header-border-color: var(--mj-border-default);\n\n /* Rows */\n --grid-row-height: 40px;\n --grid-row-bg: var(--mj-bg-surface);\n --grid-row-bg-alt: var(--mj-bg-surface-card);\n --grid-row-hover-bg: var(--mj-bg-surface-hover);\n --grid-row-selected-bg: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n --grid-row-selected-hover-bg: color-mix(in srgb, var(--mj-status-warning) 18%, var(--mj-bg-surface));\n\n /* Cells */\n --grid-cell-padding: 8px 12px;\n --grid-cell-text: var(--mj-text-primary);\n --grid-cell-border-color: var(--mj-border-subtle);\n\n /* Selection - mellow yellow to avoid conflict with blue hyperlinks */\n --grid-checkbox-color: var(--mj-brand-primary);\n --grid-selection-indicator-color: var(--mj-status-warning);\n\n /* Editing */\n --grid-edit-cell-bg: var(--mj-bg-surface);\n --grid-edit-cell-border: var(--mj-brand-primary);\n --grid-edit-cell-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n\n /* Sorting */\n --grid-sort-indicator-color: var(--mj-brand-primary);\n\n /* Toolbar */\n --grid-toolbar-bg: var(--mj-bg-surface);\n --grid-toolbar-height: 48px;\n --grid-toolbar-border-color: var(--mj-border-default);\n\n /* Loading */\n --grid-loading-overlay-bg: color-mix(in srgb, var(--mj-bg-surface) 80%, transparent);\n\n /* Empty state */\n --grid-empty-text-color: var(--mj-text-disabled);\n --grid-empty-icon-color: var(--mj-border-strong);\n\n display: block;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n}\n\n/* ========================================\n Grid Container\n ======================================== */\n\n.mj-grid-container {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--grid-border-color);\n border-radius: var(--grid-border-radius);\n background: var(--grid-background);\n overflow: hidden;\n}\n\n/* ========================================\n Toolbar\n ======================================== */\n\n.mj-grid-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n min-height: var(--grid-toolbar-height);\n padding: 0 12px;\n background: var(--grid-toolbar-bg);\n border-bottom: 1px solid var(--grid-toolbar-border-color);\n gap: 12px;\n}\n\n.toolbar-left,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-center {\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n.toolbar-search {\n display: flex;\n align-items: center;\n position: relative;\n}\n\n.search-icon {\n position: absolute;\n left: 10px;\n color: var(--mj-text-disabled);\n font-size: 13px;\n}\n\n.search-input {\n padding: 6px 30px 6px 32px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n font-size: 13px;\n width: 200px;\n transition: border-color 0.2s, box-shadow 0.2s;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--grid-selection-indicator-color);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.search-clear {\n position: absolute;\n right: 6px;\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px;\n color: var(--mj-text-disabled);\n font-size: 12px;\n}\n\n.search-clear:hover {\n color: var(--mj-text-secondary);\n}\n\n.toolbar-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-primary);\n transition: background-color 0.2s, border-color 0.2s;\n}\n\n.toolbar-button:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n}\n\n.toolbar-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.toolbar-button i {\n font-size: 14px;\n}\n\n.toolbar-button-danger {\n color: var(--mj-status-error);\n}\n\n.toolbar-button-danger:hover:not(:disabled) {\n background: var(--mj-status-error-bg);\n border-color: var(--mj-status-error);\n}\n\n.row-count,\n.selection-count {\n font-weight: 500;\n}\n\n/* ========================================\n Grid Content\n ======================================== */\n\n.mj-grid-content {\n flex: 1;\n position: relative;\n overflow: hidden;\n}\n\n.mj-grid-scroll-container {\n height: 100%;\n overflow: auto;\n}\n\n/* ========================================\n Loading Overlay\n ======================================== */\n\n.mj-grid-loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--grid-loading-overlay-bg);\n z-index: 10;\n}\n\n/* ========================================\n Error State\n ======================================== */\n\n/* ========================================\n Empty / Error State\n ======================================== */\n\n/* Flex-fill so <mj-empty-state> placeholders occupy the grid content area\n (the host centers its content but does not grow to fill the parent). */\n.mj-grid-state-fill {\n flex: 1 1 auto;\n}\n\n/* ========================================\n Header Row\n ======================================== */\n\n.mj-grid-header {\n display: flex;\n min-height: var(--grid-header-height);\n background: var(--grid-header-bg);\n border-bottom: 2px solid var(--grid-header-border-color);\n position: sticky;\n top: 0;\n z-index: 5;\n}\n\n.mj-grid-header-cell {\n display: flex;\n align-items: center;\n padding: var(--grid-cell-padding);\n font-weight: var(--grid-header-font-weight);\n color: var(--grid-header-text);\n font-size: 13px;\n user-select: none;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right: 1px solid var(--grid-cell-border-color);\n flex-shrink: 0;\n}\n\n.mj-grid-header-cell:last-child {\n border-right: none;\n}\n\n.mj-grid-header-cell.sortable {\n cursor: pointer;\n}\n\n.mj-grid-header-cell.sortable:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n\n.header-text {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.sort-indicator {\n display: flex;\n align-items: center;\n margin-left: 6px;\n color: var(--grid-sort-indicator-color);\n}\n\n.sort-indicator i {\n font-size: 12px;\n}\n\n.sort-index {\n font-size: 10px;\n margin-left: 2px;\n font-weight: normal;\n}\n\n/* ========================================\n Data Rows\n ======================================== */\n\n.mj-grid-row {\n display: flex;\n align-items: stretch;\n background: var(--grid-row-bg);\n transition: background-color 0.15s;\n cursor: default;\n}\n\n.mj-grid-row:hover {\n background: var(--grid-row-hover-bg);\n}\n\n.mj-grid-row.grid-row-alt {\n background: var(--grid-row-bg-alt);\n}\n\n.mj-grid-row.grid-row-alt:hover {\n background: var(--grid-row-hover-bg);\n}\n\n.mj-grid-row.grid-row-selected {\n background: var(--grid-row-selected-bg);\n}\n\n.mj-grid-row.grid-row-selected:hover {\n background: var(--grid-row-selected-hover-bg);\n}\n\n.mj-grid-row.grid-row-editing {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n}\n\n.mj-grid-row.grid-row-dirty {\n border-left: 3px solid var(--mj-status-warning);\n}\n\n/* ========================================\n Data Cells\n ======================================== */\n\n.mj-grid-cell {\n display: flex;\n align-items: center;\n padding: var(--grid-cell-padding);\n color: var(--grid-cell-text);\n font-size: 13px;\n overflow: hidden;\n border-right: 1px solid transparent;\n flex-shrink: 0;\n}\n\n.mj-grid-cell:last-child {\n border-right: none;\n}\n\n/* Grid lines modes */\n.grid-lines-horizontal .mj-grid-row {\n border-bottom: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-vertical .mj-grid-cell {\n border-right: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-both .mj-grid-row {\n border-bottom: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-both .mj-grid-cell {\n border-right: 1px solid var(--grid-cell-border-color);\n}\n\n/* Cell alignment */\n.mj-grid-cell.align-left {\n justify-content: flex-start;\n}\n\n.mj-grid-cell.align-center {\n justify-content: center;\n}\n\n.mj-grid-cell.align-right {\n justify-content: flex-end;\n}\n\n.cell-content {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Special cells */\n.row-number-cell {\n width: 50px;\n min-width: 50px;\n max-width: 50px;\n justify-content: center;\n color: var(--mj-text-disabled);\n font-size: 12px;\n background: var(--grid-header-bg);\n}\n\n.checkbox-cell {\n width: 40px;\n min-width: 40px;\n max-width: 40px;\n justify-content: center;\n}\n\n.checkbox-cell input[type=\"checkbox\"] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n accent-color: var(--grid-checkbox-color);\n}\n\n/* ========================================\n Virtual Scrolling\n ======================================== */\n\n.mj-grid-virtual-spacer {\n flex-shrink: 0;\n}\n\n/* ========================================\n Responsive Adjustments\n ======================================== */\n\n@media (max-width: 768px) {\n .mj-grid-toolbar {\n flex-wrap: wrap;\n padding: 8px;\n }\n\n .toolbar-search {\n order: 3;\n width: 100%;\n margin-top: 8px;\n }\n\n .search-input {\n width: 100%;\n }\n\n .toolbar-center {\n order: 2;\n }\n\n /* Hide button text on mobile */\n .toolbar-button .button-text {\n display: none;\n }\n}\n\n/* ========================================\n Toolbar Button Text\n ======================================== */\n\n.toolbar-button .button-text {\n font-size: 13px;\n}\n\n/* ========================================\n Overflow Menu\n ======================================== */\n\n.toolbar-overflow {\n position: relative;\n}\n\n.overflow-trigger {\n padding: 6px 8px !important;\n}\n\n.overflow-trigger i {\n font-size: 16px;\n}\n\n.overflow-menu {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n min-width: 220px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.08);\n z-index: 1000;\n overflow: hidden;\n}\n\n.overflow-item {\n display: flex;\n align-items: center;\n width: 100%;\n padding: 10px 16px;\n border: none;\n background: none;\n cursor: pointer;\n font-size: 14px;\n color: var(--mj-text-primary);\n text-align: left;\n gap: 12px;\n transition: background-color 0.15s;\n}\n\n.overflow-item:hover:not(:disabled) {\n background: var(--mj-bg-surface-card);\n}\n\n.overflow-item:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.overflow-item i {\n width: 18px;\n font-size: 14px;\n color: var(--mj-text-secondary);\n text-align: center;\n}\n\n.overflow-item span {\n flex: 1;\n}\n\n.overflow-divider {\n height: 1px;\n background: var(--mj-border-default);\n margin: 4px 0;\n}\n\n.overflow-section-label {\n padding: 8px 16px 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n letter-spacing: 0.5px;\n}\n\n/* Entity Actions submenu styling */\n.overflow-item.action-item i {\n color: var(--mj-brand-primary);\n}\n\n/* ========================================\n Highlight Matches\n ======================================== */\n\n::ng-deep .highlight-match {\n background-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n border-radius: 2px;\n padding: 0 1px;\n}\n\n/* ========================================\n AG Grid Customizations\n ======================================== */\n\n.mj-ag-grid {\n width: 100%;\n height: 100%;\n}\n\n/* AG Grid v35 uses JS-based theming (colorSchemeVariable) \u2014 no CSS variable overrides needed */\n\n/* Selected row styling - left indicator bar only, background handled by AG Grid theme */\n::ng-deep .ag-row-selected {\n box-shadow: inset 4px 0 0 0 var(--mj-status-warning);\n}\n\n/* Selection checkbox styling */\n::ng-deep .ag-checkbox-input-wrapper {\n width: 18px;\n height: 18px;\n}\n\n::ng-deep .ag-checkbox-input-wrapper.ag-checked::after {\n color: var(--grid-checkbox-color, var(--mj-brand-primary));\n}\n\n/* ========================================\n Visual Config: Header Styles\n ======================================== */\n\n/* Flat header (minimal) */\n.header-style-flat ::ng-deep .ag-header {\n background: var(--grid-header-bg, var(--mj-bg-surface-card));\n border-bottom: 1px solid var(--grid-header-border-color, var(--mj-border-default));\n}\n\n/* Elevated header (default - subtle shadow) */\n.header-style-elevated ::ng-deep .ag-header {\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--grid-header-border-color, var(--mj-border-default));\n}\n\n.header-style-elevated.header-shadow ::ng-deep .ag-header {\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06);\n}\n\n/* Gradient header (more prominent) */\n.header-style-gradient ::ng-deep .ag-header {\n background: var(--mj-bg-surface-sunken);\n border-bottom: none;\n}\n\n.header-style-gradient.header-shadow ::ng-deep .ag-header {\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n}\n\n/* Bold header (high contrast) */\n.header-style-bold ::ng-deep .ag-header {\n background: var(--mj-text-secondary);\n border-bottom: none;\n}\n\n.header-style-bold ::ng-deep .ag-header-cell-text {\n color: var(--mj-text-inverse);\n font-weight: 600;\n}\n\n.header-style-bold ::ng-deep .ag-header-icon {\n color: color-mix(in srgb, var(--mj-text-inverse) 70%, transparent);\n}\n\n.header-style-bold ::ng-deep .ag-header-cell:hover {\n background: color-mix(in srgb, var(--mj-text-inverse) 10%, transparent);\n}\n\n/* Header sort icons enhancement */\n::ng-deep .ag-header-cell-sorted-asc .ag-icon-asc,\n::ng-deep .ag-header-cell-sorted-desc .ag-icon-desc {\n color: var(--grid-accent-color, var(--grid-sort-indicator-color, var(--mj-brand-primary)));\n}\n\n/* ========================================\n Visual Config: Zebra Striping\n ======================================== */\n\n/* Subtle contrast */\n.alternate-rows-subtle ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.015);\n}\n\n.alternate-rows-subtle ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, var(--mj-bg-surface-hover));\n}\n\n/* Medium contrast (default) */\n.alternate-rows-medium ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.025);\n}\n\n.alternate-rows-medium ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, var(--mj-bg-surface-hover));\n}\n\n/* Strong contrast */\n.alternate-rows-strong ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n.alternate-rows-strong ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, var(--mj-bg-surface-hover));\n}\n\n/* ========================================\n Visual Config: Hover Transitions\n ======================================== */\n\n.hover-transitions ::ng-deep .ag-row {\n transition: background-color var(--grid-hover-transition, 150ms) ease;\n}\n\n.hover-transitions ::ng-deep .ag-cell {\n transition: background-color var(--grid-hover-transition, 150ms) ease;\n}\n\n/* ========================================\n Visual Config: Cell Padding\n ======================================== */\n\n.cell-padding-compact ::ng-deep .ag-cell {\n padding-left: 8px;\n padding-right: 8px;\n}\n\n.cell-padding-compact ::ng-deep .ag-header-cell {\n padding-left: 8px;\n padding-right: 8px;\n}\n\n.cell-padding-normal ::ng-deep .ag-cell {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.cell-padding-normal ::ng-deep .ag-header-cell {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.cell-padding-comfortable ::ng-deep .ag-cell {\n padding-left: 16px;\n padding-right: 16px;\n}\n\n.cell-padding-comfortable ::ng-deep .ag-header-cell {\n padding-left: 16px;\n padding-right: 16px;\n}\n\n/* ========================================\n Visual Config: Checkbox Styles\n ======================================== */\n\n/* Rounded checkbox */\n.checkbox-style-rounded ::ng-deep .ag-checkbox-input-wrapper {\n border-radius: 4px;\n}\n\n.checkbox-style-rounded ::ng-deep .ag-checkbox-input-wrapper::after {\n border-radius: 3px;\n}\n\n/* Filled checkbox */\n.checkbox-style-filled ::ng-deep .ag-checkbox-input-wrapper.ag-checked {\n background-color: var(--grid-checkbox-color, var(--mj-brand-primary));\n border-color: var(--grid-checkbox-color, var(--mj-brand-primary));\n}\n\n.checkbox-style-filled ::ng-deep .ag-checkbox-input-wrapper.ag-checked::after {\n color: var(--mj-text-inverse);\n}\n\n/* ========================================\n Cell Content Formatting\n ======================================== */\n\n/* Right-aligned cells (numbers) */\n::ng-deep .cell-align-right {\n text-align: right;\n justify-content: flex-end;\n}\n\n::ng-deep .header-align-right .ag-header-cell-label {\n justify-content: flex-end;\n}\n\n/* Empty cell placeholder */\n::ng-deep .cell-empty {\n color: var(--mj-text-disabled);\n font-style: normal;\n}\n\n/* Boolean icons */\n::ng-deep .cell-boolean-true {\n color: var(--mj-status-success);\n font-size: 14px;\n}\n\n::ng-deep .cell-boolean-false {\n color: var(--mj-text-disabled);\n font-size: 14px;\n}\n\n/* Clickable links */\n::ng-deep .cell-link {\n color: var(--grid-accent-color, var(--mj-brand-primary));\n text-decoration: none;\n transition: color 0.15s;\n font-size: inherit;\n font-family: inherit;\n line-height: inherit;\n}\n\n::ng-deep .cell-link:hover {\n color: var(--mj-brand-primary);\n text-decoration: underline;\n}\n\n/* Email cells */\n::ng-deep .cell-email {\n font-family: inherit;\n font-size: 13px;\n}\n\n/* Foreign key link cells */\n::ng-deep .cell-fk-link {\n color: var(--grid-accent-color, var(--mj-brand-primary));\n text-decoration: none;\n cursor: pointer;\n transition: color 0.15s, text-decoration 0.15s;\n}\n\n::ng-deep .cell-fk-link:hover {\n color: var(--mj-brand-primary-hover);\n text-decoration: underline;\n}\n\n::ng-deep .cell-fk-link:active {\n color: var(--mj-brand-primary-active);\n}\n\n/* URL cells */\n::ng-deep .cell-url {\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n font-size: 13px;\n}\n\n/* Phone cells */\n::ng-deep .cell-phone {\n font-variant-numeric: tabular-nums;\n letter-spacing: 0.3px;\n}\n\n/* ========================================\n Skeleton Loading Animation\n ======================================== */\n\n@keyframes skeleton-shimmer {\n 0% {\n background-position: -200px 0;\n }\n 100% {\n background-position: calc(200px + 100%) 0;\n }\n}\n\n.skeleton-row {\n display: flex;\n height: 40px;\n align-items: center;\n padding: 0 12px;\n border-bottom: 1px solid var(--grid-cell-border-color, var(--mj-border-subtle));\n}\n\n.skeleton-cell {\n height: 16px;\n border-radius: 4px;\n background: linear-gradient(\n 90deg,\n var(--mj-bg-surface-sunken) 0px,\n var(--mj-bg-surface-hover) 40px,\n var(--mj-bg-surface-sunken) 80px\n );\n background-size: 200px 100%;\n animation: skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-cell-short {\n width: 60px;\n}\n\n.skeleton-cell-medium {\n width: 120px;\n}\n\n.skeleton-cell-long {\n width: 180px;\n}\n\n/* Selection checkbox column header */\n::ng-deep .ag-header-select-all {\n margin-right: 0;\n}\n\n/* ========================================\n Row Border Enhancement\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-row {\n border-bottom: 1px solid var(--grid-cell-border-color, var(--mj-border-subtle));\n}\n\n::ng-deep .ag-theme-alpine .ag-row:last-child {\n border-bottom: none;\n}\n\n/* ========================================\n Focus States\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-cell-focus {\n border: none !important;\n outline: none !important;\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:focus {\n outline: 2px solid var(--grid-accent-color, var(--mj-brand-primary));\n outline-offset: -2px;\n}\n\n/* ========================================\n Scrollbar Styling\n ======================================== */\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-track {\n background: var(--mj-bg-surface-card);\n border-radius: 4px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 4px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-thumb:hover {\n background: var(--mj-text-disabled);\n}\n\n/* ========================================\n Pinned Column Styling\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-pinned-left-cols-container {\n border-right: 2px solid var(--grid-border-color, var(--mj-border-default));\n}\n\n::ng-deep .ag-theme-alpine .ag-pinned-right-cols-container {\n border-left: 2px solid var(--grid-border-color, var(--mj-border-default));\n}\n\n/* ========================================\n Header Cell Enhancements\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-header-cell {\n font-weight: 600;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n color: var(--mj-text-secondary);\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:hover {\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell-sortable:hover .ag-header-cell-label {\n color: var(--grid-accent-color, var(--mj-brand-primary));\n}\n\n/* Sort icon animation */\n::ng-deep .ag-theme-alpine .ag-sort-indicator-icon {\n transition: transform 0.2s ease;\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:hover .ag-sort-indicator-icon {\n transform: scale(1.1);\n}\n\n/* ========================================\n Enhanced Multi-Sort Indicators\n These styles use :host to ensure they're scoped to this component\n and !important to override AG Grid's built-in styles\n ======================================== */\n\n/* Highlight sorted column headers with eye-catching background */\n/* Ascending = blue tint */\n:host ::ng-deep .ag-header-cell-sorted-asc {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface)) !important;\n position: relative;\n}\n\n/* Descending = pink tint */\n:host ::ng-deep .ag-header-cell-sorted-desc {\n background: color-mix(in srgb, var(--mj-status-error) 12%, var(--mj-bg-surface)) !important;\n position: relative;\n}\n\n/* Bottom border accent for sorted columns - ascending = blue */\n:host ::ng-deep .ag-header-cell-sorted-asc::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: var(--mj-brand-primary);\n}\n\n/* Bottom border accent for sorted columns - descending = pink */\n:host ::ng-deep .ag-header-cell-sorted-desc::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: var(--mj-status-error);\n}\n\n/* Sorted column header text - ascending = blue, bold */\n:host ::ng-deep .ag-header-cell-sorted-asc .ag-header-cell-text {\n color: var(--mj-brand-primary) !important;\n font-weight: 700 !important;\n}\n\n/* Sorted column header text - descending = pink, bold */\n:host ::ng-deep .ag-header-cell-sorted-desc .ag-header-cell-text {\n color: var(--mj-status-error) !important;\n font-weight: 700 !important;\n}\n\n/* Sort icons - larger and more prominent */\n/* Ascending = blue */\n:host ::ng-deep .ag-sort-ascending-icon {\n color: var(--mj-brand-primary) !important;\n}\n\n/* Descending = pink/magenta */\n:host ::ng-deep .ag-sort-descending-icon {\n color: var(--mj-status-error) !important;\n}\n\n:host ::ng-deep .ag-sort-ascending-icon .ag-icon,\n:host ::ng-deep .ag-sort-descending-icon .ag-icon {\n font-size: 14px !important;\n}\n\n/* Sort order number (1, 2, 3) - show as plain number, no bubble */\n/* This avoids AG Grid bug where .ag-sort-order is always present but empty when unsorted */\n/* The number is only visible when AG Grid populates it (i.e., when column is sorted) */\n/* Default blue for ascending */\n:host ::ng-deep .ag-sort-order {\n margin-left: 4px;\n font-size: 11px !important;\n font-weight: 700 !important;\n color: var(--mj-brand-primary) !important;\n}\n\n/* Sort order number - pink for descending */\n:host ::ng-deep .ag-header-cell-sorted-desc .ag-sort-order {\n color: var(--mj-status-error) !important;\n}\n\n/* Hover state for sortable headers */\n:host ::ng-deep .ag-header-cell-sortable:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n\n:host ::ng-deep .ag-header-cell-sortable:hover .ag-header-cell-text {\n color: var(--mj-brand-primary);\n}\n\n/* ========================================\n Aggregate Summary Row\n ======================================== */\n\n.mj-aggregate-summary {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n font-size: 13px;\n min-height: 44px;\n}\n\n.aggregate-summary-content {\n display: flex;\n flex-wrap: wrap;\n gap: 20px;\n align-items: center;\n}\n\n.aggregate-summary-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n}\n\n.aggregate-summary-item i {\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.agg-summary-label {\n color: var(--mj-text-muted);\n font-weight: 500;\n}\n\n.agg-summary-value {\n color: var(--mj-text-primary);\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n}\n\n.aggregate-loading {\n display: flex;\n align-items: center;\n color: var(--mj-text-disabled);\n}\n\n.aggregate-loading i {\n font-size: 14px;\n}\n\n/* ========================================\n AGGREGATE CARDS (displayed above grid)\n ======================================== */\n\n.mj-aggregate-cards {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.aggregate-card {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n min-width: 140px;\n transition: all 0.15s ease;\n}\n\n.aggregate-card:hover {\n border-color: var(--mj-border-strong);\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);\n}\n\n.aggregate-card-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n border-radius: 10px;\n color: var(--mj-brand-primary);\n font-size: 16px;\n flex-shrink: 0;\n}\n\n.aggregate-card-content {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.aggregate-card-label {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.aggregate-card-value {\n font-size: 18px;\n font-weight: 700;\n color: var(--mj-text-primary);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.aggregate-card-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 12px;\n color: var(--mj-text-disabled);\n}\n\n.aggregate-card-loading i {\n font-size: 16px;\n}\n\n/* ========================================\n Text Wrapping Styles\n ======================================== */\n\n/* Cell class for wrapped text */\n::ng-deep .cell-wrap-text {\n white-space: normal !important;\n word-wrap: break-word;\n line-height: 1.4;\n padding-top: 8px !important;\n padding-bottom: 8px !important;\n}\n\n/* Ensure AG Grid cells allow wrapping */\n::ng-deep .ag-cell.cell-wrap-text {\n overflow: visible;\n text-overflow: clip;\n}\n\n/* Maintain minimum row height when wrapping */\n::ng-deep .ag-row[style*=\"auto-height\"] {\n min-height: 40px;\n}\n\n/* ========================================\n Width filler column ([FillWidth])\n ========================================\n The trailing filler exists so row banding and horizontal rules reach the container's right edge\n instead of stopping wherever the last real column ends. For that to read as \"the table extends\"\n rather than \"there is an empty column here\", it must lose every affordance that makes a column\n look like a column: its own vertical rule, the header hover/menu, and the resize handle. What is\n left is bare row background \u2014 which is exactly the point. */\n::ng-deep .ag-header-cell.mj-grid-filler-header,\n::ng-deep .ag-cell.mj-grid-filler-cell {\n border-left: none !important;\n border-right: none !important;\n cursor: default;\n}\n\n/* The hover tint is OURS (see \".ag-theme-alpine .ag-header-cell:hover\" above), not AG Grid's, so it\n has to be switched off here or the filler lights up as though it were interactive. Everything else\n the filler needs suppressed \u2014 resize grip, header menu, moving, tool-panel entry \u2014 is handled by\n native colDef flags on the column itself, not by CSS. */\n::ng-deep .ag-header-cell.mj-grid-filler-header:hover {\n background-color: transparent !important;\n}\n\n/* AG Grid injects cellRenderer HTML without Angular attributes, so these\n must pierce encapsulation or the image renders at intrinsic SVG size. */\n::ng-deep .ag-cell.mj-grid-image-cell {\n display: flex !important;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n padding: 4px !important;\n}\n::ng-deep .ag-header-cell.mj-grid-image-header .ag-header-cell-label {\n justify-content: center;\n}\n::ng-deep .cell-image-link {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n overflow: hidden;\n border-radius: 50%;\n background: var(--mj-bg-surface-sunken);\n flex-shrink: 0;\n}\n::ng-deep .cell-image {\n width: 100%;\n height: 100%;\n max-width: 28px;\n max-height: 28px;\n object-fit: cover;\n object-position: center;\n display: block;\n}\n"] }]
5369
+ ], template: "<!-- Grid Container -->\n<div\n #gridContainer\n [class]=\"gridContainerClasses.join(' ')\"\n [style.height]=\"gridHeightStyle\">\n\n <!-- Toolbar -->\n @if (ShowToolbar) {\n <div class=\"mj-grid-toolbar\">\n <div class=\"toolbar-left\">\n <!-- Search -->\n @if (ShowSearch) {\n <div class=\"toolbar-search\">\n <i class=\"fa-solid fa-search search-icon\"></i>\n <input\n type=\"text\"\n class=\"search-input\"\n [placeholder]=\"ToolbarConfig.searchPlaceholder || 'Search...'\"\n [value]=\"FilterText\"\n (input)=\"FilterText = $any($event.target).value\" />\n @if (FilterText) {\n <button\n class=\"search-clear\"\n (click)=\"FilterText = ''\"\n aria-label=\"Clear search\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n }\n <!-- Custom Left Buttons -->\n @for (button of ToolbarConfig.customButtons; track button) {\n @if (button.position !== 'right' && isButtonVisible(button)) {\n <button\n class=\"toolbar-button\"\n [class]=\"button.cssClass\"\n [disabled]=\"isButtonDisabled(button)\"\n [title]=\"button.tooltip || ''\"\n (click)=\"onToolbarButtonClick(button)\">\n @if (button.icon) {\n <i [class]=\"button.icon\"></i>\n }\n @if (button.text) {\n <span>{{ button.text }}</span>\n }\n </button>\n }\n }\n </div>\n <div class=\"toolbar-center\">\n <!-- Row Count -->\n @if (ToolbarConfig.showRowCount !== false) {\n <span class=\"row-count\">\n {{ totalRowCount }} {{ totalRowCount === 1 ? 'row' : 'rows' }}\n </span>\n }\n <!-- Selection Count -->\n @if (ToolbarConfig.showSelectionCount && SelectedKeys.length > 0) {\n <span class=\"selection-count\">\n ({{ SelectedKeys.length }} selected)\n </span>\n }\n </div>\n <div class=\"toolbar-right\">\n <!-- Recycle Bin chip \u2014 auto-hides when no deleted records, no tracking,\n or user lacks Delete permission. -->\n @if (ShowRecycleBin) {\n <mj-recycle-bin-chip [EntityName]=\"entityInfoName\"></mj-recycle-bin-chip>\n }\n <!-- New/Add Button (predefined) -->\n @if (ShowNewButton) {\n <button\n class=\"toolbar-button\"\n title=\"Create new record\"\n (click)=\"onAddClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n <span class=\"button-text\">New</span>\n </button>\n }\n <!-- Refresh Button (predefined) -->\n @if (ShowRefreshButton) {\n <button\n class=\"toolbar-button\"\n title=\"Refresh data\"\n [disabled]=\"loading\"\n (click)=\"onRefreshClick()\">\n <i class=\"fa-solid fa-arrows-rotate\" [class.fa-spin]=\"loading\"></i>\n <span class=\"button-text\">Refresh</span>\n </button>\n }\n <!-- Export Button (predefined) -->\n @if (ShowExportButton) {\n <button\n class=\"toolbar-button\"\n title=\"Export\"\n [disabled]=\"IsPreparingExport\"\n (click)=\"onExportClick()\">\n <i class=\"fa-solid\" [class.fa-file-excel]=\"!IsPreparingExport\" [class.fa-spinner]=\"IsPreparingExport\" [class.fa-spin]=\"IsPreparingExport\"></i>\n <span class=\"button-text\">Export</span>\n </button>\n }\n <!-- Delete Button (predefined) -->\n @if (ShowDeleteButton && HasSelection) {\n <button\n class=\"toolbar-button toolbar-button-danger\"\n title=\"Delete selected records\"\n (click)=\"onDeleteClick()\">\n <i class=\"fa-solid fa-trash\"></i>\n <span class=\"button-text\">Delete</span>\n </button>\n }\n <!-- Compare Button (predefined) -->\n @if (ShowCompareButton) {\n <button\n class=\"toolbar-button\"\n title=\"Compare selected records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onCompareClick()\">\n <i class=\"fa-solid fa-code-compare\"></i>\n <span class=\"button-text\">Compare</span>\n </button>\n }\n <!-- Merge Button (predefined) -->\n @if (ShowMergeButton) {\n <button\n class=\"toolbar-button\"\n title=\"Merge selected records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onMergeClick()\">\n <i class=\"fa-solid fa-code-merge\"></i>\n <span class=\"button-text\">Merge</span>\n </button>\n }\n <!-- Add to List Button (predefined) -->\n @if (ShowAddToListButton) {\n <button\n class=\"toolbar-button\"\n [title]=\"HasSelection ? 'Add selected records to a list' : 'Add all matching records to a list'\"\n [disabled]=\"(!HasSelection && !ExportDataProvider) || IsPreparingAddToList\"\n (click)=\"onAddToListClick()\">\n <i class=\"fa-solid\" [class.fa-list-check]=\"!IsPreparingAddToList\" [class.fa-spinner]=\"IsPreparingAddToList\" [class.fa-spin]=\"IsPreparingAddToList\"></i>\n <span class=\"button-text\">Add to List</span>\n </button>\n }\n <!-- Search for Duplicates Button (predefined) -->\n @if (ShowDuplicateSearchButton) {\n <button\n class=\"toolbar-button\"\n title=\"Search for duplicate records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onDuplicateSearchClick()\">\n <i class=\"fa-solid fa-magnifying-glass-plus\"></i>\n <span class=\"button-text\">Find Duplicates</span>\n </button>\n }\n <!-- Communication Button (predefined) -->\n @if (ShowCommunicationButton && EntitySupportsCommunication) {\n <button\n class=\"toolbar-button\"\n title=\"Send message to selected records\"\n [disabled]=\"!HasSelection\"\n (click)=\"onCommunicationClick()\">\n <i class=\"fa-solid fa-envelope\"></i>\n <span class=\"button-text\">Send Message</span>\n </button>\n }\n <!-- Legacy ToolbarConfig buttons -->\n <!-- Add Button (legacy) -->\n @if (ToolbarConfig.showAdd && AllowAdd && !ShowNewButton) {\n <button\n class=\"toolbar-button\"\n title=\"Add New\"\n (click)=\"onAddClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n }\n <!-- Refresh Button (legacy) -->\n @if (ToolbarConfig.showRefresh !== false && !ShowRefreshButton) {\n <button\n class=\"toolbar-button\"\n title=\"Refresh\"\n [disabled]=\"loading\"\n (click)=\"onRefreshClick()\">\n <i class=\"fa-solid fa-refresh\" [class.fa-spin]=\"loading\"></i>\n </button>\n }\n <!-- Delete Button (legacy) -->\n @if (ToolbarConfig.showDelete && AllowDelete && HasSelection && !ShowDeleteButton) {\n <button\n class=\"toolbar-button toolbar-button-danger\"\n title=\"Delete Selected\"\n (click)=\"onDeleteClick()\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n }\n <!-- Export Button (legacy) -->\n @if (ToolbarConfig.showExport && !ShowExportButton) {\n <button\n class=\"toolbar-button\"\n title=\"Export\"\n (click)=\"onExportClick()\">\n <i class=\"fa-solid fa-download\"></i>\n </button>\n }\n <!-- Column Chooser Button -->\n @if (ToolbarConfig.showColumnChooser && AllowColumnToggle) {\n <button\n class=\"toolbar-button\"\n title=\"Column Chooser\"\n (click)=\"onColumnChooserClick()\">\n <i class=\"fa-solid fa-columns\"></i>\n </button>\n }\n <!-- Custom Right Buttons -->\n @for (button of ToolbarConfig.customButtons; track button) {\n @if (button.position === 'right' && isButtonVisible(button)) {\n <button\n class=\"toolbar-button\"\n [class]=\"button.cssClass\"\n [disabled]=\"isButtonDisabled(button)\"\n [title]=\"button.tooltip || ''\"\n (click)=\"onToolbarButtonClick(button)\">\n @if (button.icon) {\n <i [class]=\"button.icon\"></i>\n }\n @if (button.text) {\n <span>{{ button.text }}</span>\n }\n </button>\n }\n }\n <!-- Overflow Menu -->\n @if (hasOverflowMenuItems) {\n <div class=\"toolbar-overflow\" (click)=\"$event.stopPropagation()\">\n <button\n class=\"toolbar-button overflow-trigger\"\n title=\"More actions\"\n (click)=\"toggleOverflowMenu()\">\n <i class=\"fa-solid fa-ellipsis-vertical\"></i>\n </button>\n @if (showOverflowMenu) {\n <div class=\"overflow-menu\" [@fadeIn]>\n <!-- Export (if in overflow) -->\n @if (showExportInOverflow) {\n <button class=\"overflow-item\" (click)=\"onExportClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-file-excel\"></i>\n <span>Export</span>\n </button>\n }\n <!-- Entity Actions -->\n @if (ShowEntityActionButtons && EntityActions.length > 0) {\n <div class=\"overflow-divider\"></div>\n <div class=\"overflow-section-label\">Actions</div>\n @for (action of EntityActions; track action) {\n <button\n class=\"overflow-item\"\n [disabled]=\"!isEntityActionEnabled(action)\"\n (click)=\"onEntityActionClick(action); closeOverflowMenu()\">\n <i [class]=\"action.icon || 'fa-solid fa-bolt'\"></i>\n <span>{{ action.name }}</span>\n </button>\n }\n }\n <!-- Column Chooser (if in overflow) -->\n @if (showColumnChooserInOverflow) {\n <div class=\"overflow-divider\"></div>\n }\n @if (showColumnChooserInOverflow) {\n <button class=\"overflow-item\" (click)=\"onColumnChooserClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-columns\"></i>\n <span>Manage Columns</span>\n </button>\n }\n <!-- Selection-dependent actions in overflow -->\n @if (HasSelection && hasSelectionDependentOverflowActions) {\n <div class=\"overflow-divider\"></div>\n @if (showCommunicationInOverflow) {\n <button class=\"overflow-item\" (click)=\"onCommunicationClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-envelope\"></i>\n <span>Send Message</span>\n </button>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Aggregate Cards (displayed as summary cards above the grid) -->\n @if (ShowAggregatePanel) {\n <div class=\"mj-aggregate-cards\">\n @for (agg of CardAggregates; track agg) {\n <div class=\"aggregate-card\">\n @if (agg.icon) {\n <div class=\"aggregate-card-icon\">\n <i [class]=\"agg.icon\"></i>\n </div>\n }\n <div class=\"aggregate-card-content\">\n <span class=\"aggregate-card-label\">{{ agg.label }}</span>\n <span class=\"aggregate-card-value\">{{ getAggregateValue(agg) }}</span>\n </div>\n </div>\n }\n @if (AggregatesLoading) {\n <div class=\"aggregate-card-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </div>\n }\n </div>\n }\n\n <!-- Grid Content -->\n <div class=\"mj-grid-content\">\n <!-- Loading Overlay -->\n @if (loading && rowData.length === 0) {\n <div class=\"mj-grid-loading-overlay\">\n <mj-loading text=\"Loading...\"></mj-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (errorMessage && !loading) {\n <mj-empty-state\n class=\"mj-grid-state-fill\"\n [Size]=\"EmptyStateSize\"\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n Title=\"Couldn't load this data\"\n [Message]=\"FriendlyErrorMessage\"\n ActionText=\"Retry\"\n ActionIcon=\"fa-solid fa-arrows-rotate\"\n (Action)=\"Refresh()\" />\n }\n\n <!-- Empty State -->\n @if (!loading && !errorMessage && rowData.length === 0) {\n <mj-empty-state\n class=\"mj-grid-state-fill\"\n [Size]=\"EmptyStateSize\"\n Icon=\"fa-solid fa-inbox\"\n [Title]=\"EntityInfo ? 'No ' + EntityInfo.DisplayNamePlural + ' to display' : 'No data to display'\" />\n }\n\n <!-- AG Grid (Client-side mode) -->\n @if (!errorMessage && PaginationMode === 'client' && (rowData.length > 0 || loading)) {\n <ag-grid-angular\n class=\"mj-ag-grid ag-theme-alpine\"\n [theme]=\"agGridTheme\"\n [columnDefs]=\"agColumnDefs\"\n [rowData]=\"rowData\"\n [defaultColDef]=\"defaultColDef\"\n [rowSelection]=\"agRowSelection\"\n [getRowId]=\"getRowId\"\n [suppressCellFocus]=\"true\"\n [rowHeight]=\"RowHeight\"\n [headerHeight]=\"ShowHeader ? undefined : 0\"\n (gridReady)=\"onGridReady($event)\"\n (cellClicked)=\"onAgCellClicked($event)\"\n (rowClicked)=\"onAgRowClicked($event)\"\n (rowDoubleClicked)=\"onAgRowDoubleClicked($event)\"\n (sortChanged)=\"onAgSortChanged($event)\"\n (selectionChanged)=\"onAgSelectionChanged($event)\"\n (columnResized)=\"onAgColumnResized($event)\"\n (columnMoved)=\"onAgColumnMoved($event)\">\n </ag-grid-angular>\n }\n\n <!-- AG Grid (Infinite Scroll mode) -->\n @if (!errorMessage && PaginationMode === 'infinite') {\n <ag-grid-angular\n class=\"mj-ag-grid ag-theme-alpine\"\n [theme]=\"agGridTheme\"\n [columnDefs]=\"agColumnDefs\"\n [defaultColDef]=\"defaultColDef\"\n [rowSelection]=\"agRowSelection\"\n [getRowId]=\"getRowId\"\n [suppressCellFocus]=\"true\"\n [rowHeight]=\"RowHeight\"\n [headerHeight]=\"ShowHeader ? undefined : 0\"\n [rowModelType]=\"'infinite'\"\n [cacheBlockSize]=\"CacheBlockSize\"\n [maxBlocksInCache]=\"MaxBlocksInCache\"\n [infiniteInitialRowCount]=\"1\"\n [cacheOverflowSize]=\"2\"\n (gridReady)=\"onGridReady($event)\"\n (cellClicked)=\"onAgCellClicked($event)\"\n (rowClicked)=\"onAgRowClicked($event)\"\n (rowDoubleClicked)=\"onAgRowDoubleClicked($event)\"\n (sortChanged)=\"onAgSortChanged($event)\"\n (selectionChanged)=\"onAgSelectionChanged($event)\"\n (columnResized)=\"onAgColumnResized($event)\"\n (columnMoved)=\"onAgColumnMoved($event)\">\n </ag-grid-angular>\n }\n\n </div>\n\n <!-- Pagination (opt-in via ShowPager input) -->\n @if (ShowPager) {\n <mj-pagination\n [TotalRowCount]=\"totalRowCount\"\n [PageNumber]=\"PagerPageNumber\"\n [PageSize]=\"PageSize\"\n [IsLoading]=\"loading\"\n (PageChange)=\"PageChange.emit($event)\">\n </mj-pagination>\n }\n\n <!-- Aggregate Summary Row (outside mj-grid-content so it's always visible) -->\n @if (ShowAggregateSummary) {\n <div class=\"mj-aggregate-summary\">\n <div class=\"aggregate-summary-content\">\n @for (agg of ColumnAggregates; track agg) {\n <div class=\"aggregate-summary-item\">\n @if (agg.icon) {\n <i [class]=\"agg.icon\"></i>\n }\n <span class=\"agg-summary-label\">{{ agg.label }}:</span>\n <span class=\"agg-summary-value\">{{ getAggregateValue(agg) }}</span>\n </div>\n }\n </div>\n @if (AggregatesLoading) {\n <div class=\"aggregate-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </div>\n }\n </div>\n }\n</div>\n\n<!-- Export Dialog -->\n<mj-export-dialog\n [visible]=\"showExportDialog\"\n [config]=\"exportDialogConfig\"\n (closed)=\"onExportDialogClosed($event)\">\n</mj-export-dialog>\n\n<!-- Runtime-UX driver for an entity action that names a RuntimeUXDriverClass (e.g. the Record Process\n bulk-update runner). Mounted in-place over the grid \u2014 no parent wiring required. -->\n@if (ActiveRuntimeDriver) {\n <mj-entity-action-ux-host\n [DriverClass]=\"ActiveRuntimeDriver.DriverClass\"\n [Context]=\"ActiveRuntimeDriver.Context\"\n (Completed)=\"OnRuntimeDriverCompleted($event)\"\n (Cancelled)=\"OnRuntimeDriverCancelled()\">\n </mj-entity-action-ux-host>\n}\n", styles: ["/* ========================================\n CSS Custom Properties (Theme Variables)\n ======================================== */\n\n:host {\n /* Grid container */\n --grid-border-color: var(--mj-border-default);\n --grid-border-radius: 0px;\n --grid-background: var(--mj-bg-surface);\n\n /* Header */\n --grid-header-bg: var(--mj-bg-surface-card);\n --grid-header-text: var(--mj-text-primary);\n --grid-header-font-weight: 600;\n --grid-header-height: 40px;\n --grid-header-border-color: var(--mj-border-default);\n\n /* Rows */\n --grid-row-height: 40px;\n --grid-row-bg: var(--mj-bg-surface);\n --grid-row-bg-alt: var(--mj-bg-surface-card);\n --grid-row-hover-bg: var(--mj-bg-surface-hover);\n --grid-row-selected-bg: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n --grid-row-selected-hover-bg: color-mix(in srgb, var(--mj-status-warning) 18%, var(--mj-bg-surface));\n\n /* Cells */\n --grid-cell-padding: 8px 12px;\n --grid-cell-text: var(--mj-text-primary);\n --grid-cell-border-color: var(--mj-border-subtle);\n\n /* Selection - mellow yellow to avoid conflict with blue hyperlinks */\n --grid-checkbox-color: var(--mj-brand-primary);\n --grid-selection-indicator-color: var(--mj-status-warning);\n\n /* Editing */\n --grid-edit-cell-bg: var(--mj-bg-surface);\n --grid-edit-cell-border: var(--mj-brand-primary);\n --grid-edit-cell-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n\n /* Sorting */\n --grid-sort-indicator-color: var(--mj-brand-primary);\n\n /* Toolbar */\n --grid-toolbar-bg: var(--mj-bg-surface);\n --grid-toolbar-height: 48px;\n --grid-toolbar-border-color: var(--mj-border-default);\n\n /* Loading */\n --grid-loading-overlay-bg: color-mix(in srgb, var(--mj-bg-surface) 80%, transparent);\n\n /* Empty state */\n --grid-empty-text-color: var(--mj-text-disabled);\n --grid-empty-icon-color: var(--mj-border-strong);\n\n display: block;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n}\n\n/* ========================================\n Grid Container\n ======================================== */\n\n.mj-grid-container {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--grid-border-color);\n border-radius: var(--grid-border-radius);\n background: var(--grid-background);\n overflow: hidden;\n}\n\n/* ========================================\n Toolbar\n ======================================== */\n\n.mj-grid-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n min-height: var(--grid-toolbar-height);\n padding: 0 12px;\n background: var(--grid-toolbar-bg);\n border-bottom: 1px solid var(--grid-toolbar-border-color);\n gap: 12px;\n}\n\n.toolbar-left,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-center {\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n.toolbar-search {\n display: flex;\n align-items: center;\n position: relative;\n}\n\n.search-icon {\n position: absolute;\n left: 10px;\n color: var(--mj-text-disabled);\n font-size: 13px;\n}\n\n.search-input {\n padding: 6px 30px 6px 32px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n font-size: 13px;\n width: 200px;\n transition: border-color 0.2s, box-shadow 0.2s;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--grid-selection-indicator-color);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.search-clear {\n position: absolute;\n right: 6px;\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px;\n color: var(--mj-text-disabled);\n font-size: 12px;\n}\n\n.search-clear:hover {\n color: var(--mj-text-secondary);\n}\n\n.toolbar-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-primary);\n transition: background-color 0.2s, border-color 0.2s;\n}\n\n.toolbar-button:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n}\n\n.toolbar-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.toolbar-button i {\n font-size: 14px;\n}\n\n.toolbar-button-danger {\n color: var(--mj-status-error);\n}\n\n.toolbar-button-danger:hover:not(:disabled) {\n background: var(--mj-status-error-bg);\n border-color: var(--mj-status-error);\n}\n\n.row-count,\n.selection-count {\n font-weight: 500;\n}\n\n/* ========================================\n Grid Content\n ======================================== */\n\n.mj-grid-content {\n flex: 1;\n position: relative;\n overflow: hidden;\n}\n\n.mj-grid-scroll-container {\n height: 100%;\n overflow: auto;\n}\n\n/* ========================================\n Loading Overlay\n ======================================== */\n\n.mj-grid-loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--grid-loading-overlay-bg);\n z-index: 10;\n}\n\n/* ========================================\n Error State\n ======================================== */\n\n/* ========================================\n Empty / Error State\n ======================================== */\n\n/* Flex-fill so <mj-empty-state> placeholders occupy the grid content area\n (the host centers its content but does not grow to fill the parent). */\n.mj-grid-state-fill {\n flex: 1 1 auto;\n}\n\n/* ========================================\n Header Row\n ======================================== */\n\n.mj-grid-header {\n display: flex;\n min-height: var(--grid-header-height);\n background: var(--grid-header-bg);\n border-bottom: 2px solid var(--grid-header-border-color);\n position: sticky;\n top: 0;\n z-index: 5;\n}\n\n.mj-grid-header-cell {\n display: flex;\n align-items: center;\n padding: var(--grid-cell-padding);\n font-weight: var(--grid-header-font-weight);\n color: var(--grid-header-text);\n font-size: 13px;\n user-select: none;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right: 1px solid var(--grid-cell-border-color);\n flex-shrink: 0;\n}\n\n.mj-grid-header-cell:last-child {\n border-right: none;\n}\n\n.mj-grid-header-cell.sortable {\n cursor: pointer;\n}\n\n.mj-grid-header-cell.sortable:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n\n.header-text {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.sort-indicator {\n display: flex;\n align-items: center;\n margin-left: 6px;\n color: var(--grid-sort-indicator-color);\n}\n\n.sort-indicator i {\n font-size: 12px;\n}\n\n.sort-index {\n font-size: 10px;\n margin-left: 2px;\n font-weight: normal;\n}\n\n/* ========================================\n Data Rows\n ======================================== */\n\n.mj-grid-row {\n display: flex;\n align-items: stretch;\n background: var(--grid-row-bg);\n transition: background-color 0.15s;\n cursor: default;\n}\n\n.mj-grid-row:hover {\n background: var(--grid-row-hover-bg);\n}\n\n.mj-grid-row.grid-row-alt {\n background: var(--grid-row-bg-alt);\n}\n\n.mj-grid-row.grid-row-alt:hover {\n background: var(--grid-row-hover-bg);\n}\n\n.mj-grid-row.grid-row-selected {\n background: var(--grid-row-selected-bg);\n}\n\n.mj-grid-row.grid-row-selected:hover {\n background: var(--grid-row-selected-hover-bg);\n}\n\n.mj-grid-row.grid-row-editing {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n}\n\n.mj-grid-row.grid-row-dirty {\n border-left: 3px solid var(--mj-status-warning);\n}\n\n/* ========================================\n Data Cells\n ======================================== */\n\n.mj-grid-cell {\n display: flex;\n align-items: center;\n padding: var(--grid-cell-padding);\n color: var(--grid-cell-text);\n font-size: 13px;\n overflow: hidden;\n border-right: 1px solid transparent;\n flex-shrink: 0;\n}\n\n.mj-grid-cell:last-child {\n border-right: none;\n}\n\n/* Grid lines modes */\n.grid-lines-horizontal .mj-grid-row {\n border-bottom: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-vertical .mj-grid-cell {\n border-right: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-both .mj-grid-row {\n border-bottom: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-both .mj-grid-cell {\n border-right: 1px solid var(--grid-cell-border-color);\n}\n\n/* Cell alignment */\n.mj-grid-cell.align-left {\n justify-content: flex-start;\n}\n\n.mj-grid-cell.align-center {\n justify-content: center;\n}\n\n.mj-grid-cell.align-right {\n justify-content: flex-end;\n}\n\n.cell-content {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Special cells */\n.row-number-cell {\n width: 50px;\n min-width: 50px;\n max-width: 50px;\n justify-content: center;\n color: var(--mj-text-disabled);\n font-size: 12px;\n background: var(--grid-header-bg);\n}\n\n.checkbox-cell {\n width: 40px;\n min-width: 40px;\n max-width: 40px;\n justify-content: center;\n}\n\n.checkbox-cell input[type=\"checkbox\"] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n accent-color: var(--grid-checkbox-color);\n}\n\n/* ========================================\n Virtual Scrolling\n ======================================== */\n\n.mj-grid-virtual-spacer {\n flex-shrink: 0;\n}\n\n/* ========================================\n Responsive Adjustments\n ======================================== */\n\n@media (max-width: 768px) {\n .mj-grid-toolbar {\n flex-wrap: wrap;\n padding: 8px;\n }\n\n .toolbar-search {\n order: 3;\n width: 100%;\n margin-top: 8px;\n }\n\n .search-input {\n width: 100%;\n }\n\n .toolbar-center {\n order: 2;\n }\n\n /* Hide button text on mobile */\n .toolbar-button .button-text {\n display: none;\n }\n}\n\n/* ========================================\n Toolbar Button Text\n ======================================== */\n\n.toolbar-button .button-text {\n font-size: 13px;\n}\n\n/* ========================================\n Overflow Menu\n ======================================== */\n\n.toolbar-overflow {\n position: relative;\n}\n\n.overflow-trigger {\n padding: 6px 8px !important;\n}\n\n.overflow-trigger i {\n font-size: 16px;\n}\n\n.overflow-menu {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n min-width: 220px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.08);\n z-index: 1000;\n overflow: hidden;\n}\n\n.overflow-item {\n display: flex;\n align-items: center;\n width: 100%;\n padding: 10px 16px;\n border: none;\n background: none;\n cursor: pointer;\n font-size: 14px;\n color: var(--mj-text-primary);\n text-align: left;\n gap: 12px;\n transition: background-color 0.15s;\n}\n\n.overflow-item:hover:not(:disabled) {\n background: var(--mj-bg-surface-card);\n}\n\n.overflow-item:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.overflow-item i {\n width: 18px;\n font-size: 14px;\n color: var(--mj-text-secondary);\n text-align: center;\n}\n\n.overflow-item span {\n flex: 1;\n}\n\n.overflow-divider {\n height: 1px;\n background: var(--mj-border-default);\n margin: 4px 0;\n}\n\n.overflow-section-label {\n padding: 8px 16px 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n letter-spacing: 0.5px;\n}\n\n/* Entity Actions submenu styling */\n.overflow-item.action-item i {\n color: var(--mj-brand-primary);\n}\n\n/* ========================================\n Highlight Matches\n ======================================== */\n\n::ng-deep .highlight-match {\n background-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n border-radius: 2px;\n padding: 0 1px;\n}\n\n/* ========================================\n AG Grid Customizations\n ======================================== */\n\n.mj-ag-grid {\n width: 100%;\n height: 100%;\n}\n\n/* AG Grid v35 uses JS-based theming (colorSchemeVariable) \u2014 no CSS variable overrides needed */\n\n/* Selected row styling - left indicator bar only, background handled by AG Grid theme */\n::ng-deep .ag-row-selected {\n box-shadow: inset 4px 0 0 0 var(--mj-status-warning);\n}\n\n/* Selection checkbox styling */\n::ng-deep .ag-checkbox-input-wrapper {\n width: 18px;\n height: 18px;\n}\n\n::ng-deep .ag-checkbox-input-wrapper.ag-checked::after {\n color: var(--grid-checkbox-color, var(--mj-brand-primary));\n}\n\n/* ========================================\n Visual Config: Header Styles\n ======================================== */\n\n/* Flat header (minimal) */\n.header-style-flat ::ng-deep .ag-header {\n background: var(--grid-header-bg, var(--mj-bg-surface-card));\n border-bottom: 1px solid var(--grid-header-border-color, var(--mj-border-default));\n}\n\n/* Elevated header (default - subtle shadow) */\n.header-style-elevated ::ng-deep .ag-header {\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--grid-header-border-color, var(--mj-border-default));\n}\n\n.header-style-elevated.header-shadow ::ng-deep .ag-header {\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06);\n}\n\n/* Gradient header (more prominent) */\n.header-style-gradient ::ng-deep .ag-header {\n background: var(--mj-bg-surface-sunken);\n border-bottom: none;\n}\n\n.header-style-gradient.header-shadow ::ng-deep .ag-header {\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n}\n\n/* Bold header (high contrast) */\n.header-style-bold ::ng-deep .ag-header {\n background: var(--mj-text-secondary);\n border-bottom: none;\n}\n\n.header-style-bold ::ng-deep .ag-header-cell-text {\n color: var(--mj-text-inverse);\n font-weight: 600;\n}\n\n.header-style-bold ::ng-deep .ag-header-icon {\n color: color-mix(in srgb, var(--mj-text-inverse) 70%, transparent);\n}\n\n.header-style-bold ::ng-deep .ag-header-cell:hover {\n background: color-mix(in srgb, var(--mj-text-inverse) 10%, transparent);\n}\n\n/* Header sort icons enhancement */\n::ng-deep .ag-header-cell-sorted-asc .ag-icon-asc,\n::ng-deep .ag-header-cell-sorted-desc .ag-icon-desc {\n color: var(--grid-accent-color, var(--grid-sort-indicator-color, var(--mj-brand-primary)));\n}\n\n/* ========================================\n Visual Config: Zebra Striping\n ======================================== */\n\n/* Subtle contrast */\n.alternate-rows-subtle ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.015);\n}\n\n.alternate-rows-subtle ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, var(--mj-bg-surface-hover));\n}\n\n/* Medium contrast (default) */\n.alternate-rows-medium ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.025);\n}\n\n.alternate-rows-medium ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, var(--mj-bg-surface-hover));\n}\n\n/* Strong contrast */\n.alternate-rows-strong ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n.alternate-rows-strong ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, var(--mj-bg-surface-hover));\n}\n\n/* ========================================\n Visual Config: Hover Transitions\n ======================================== */\n\n.hover-transitions ::ng-deep .ag-row {\n transition: background-color var(--grid-hover-transition, 150ms) ease;\n}\n\n.hover-transitions ::ng-deep .ag-cell {\n transition: background-color var(--grid-hover-transition, 150ms) ease;\n}\n\n/* ========================================\n Visual Config: Cell Padding\n ======================================== */\n\n.cell-padding-compact ::ng-deep .ag-cell {\n padding-left: 8px;\n padding-right: 8px;\n}\n\n.cell-padding-compact ::ng-deep .ag-header-cell {\n padding-left: 8px;\n padding-right: 8px;\n}\n\n.cell-padding-normal ::ng-deep .ag-cell {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.cell-padding-normal ::ng-deep .ag-header-cell {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.cell-padding-comfortable ::ng-deep .ag-cell {\n padding-left: 16px;\n padding-right: 16px;\n}\n\n.cell-padding-comfortable ::ng-deep .ag-header-cell {\n padding-left: 16px;\n padding-right: 16px;\n}\n\n/* ========================================\n Visual Config: Checkbox Styles\n ======================================== */\n\n/* Rounded checkbox */\n.checkbox-style-rounded ::ng-deep .ag-checkbox-input-wrapper {\n border-radius: 4px;\n}\n\n.checkbox-style-rounded ::ng-deep .ag-checkbox-input-wrapper::after {\n border-radius: 3px;\n}\n\n/* Filled checkbox */\n.checkbox-style-filled ::ng-deep .ag-checkbox-input-wrapper.ag-checked {\n background-color: var(--grid-checkbox-color, var(--mj-brand-primary));\n border-color: var(--grid-checkbox-color, var(--mj-brand-primary));\n}\n\n.checkbox-style-filled ::ng-deep .ag-checkbox-input-wrapper.ag-checked::after {\n color: var(--mj-text-inverse);\n}\n\n/* ========================================\n Cell Content Formatting\n ======================================== */\n\n/* Right-aligned cells (numbers) */\n::ng-deep .cell-align-right {\n text-align: right;\n justify-content: flex-end;\n}\n\n::ng-deep .header-align-right .ag-header-cell-label {\n justify-content: flex-end;\n}\n\n/* Empty cell placeholder */\n::ng-deep .cell-empty {\n color: var(--mj-text-disabled);\n font-style: normal;\n}\n\n/* Boolean icons */\n::ng-deep .cell-boolean-true {\n color: var(--mj-status-success);\n font-size: 14px;\n}\n\n::ng-deep .cell-boolean-false {\n color: var(--mj-text-disabled);\n font-size: 14px;\n}\n\n/* Clickable links */\n::ng-deep .cell-link {\n color: var(--grid-accent-color, var(--mj-brand-primary));\n text-decoration: none;\n transition: color 0.15s;\n font-size: inherit;\n font-family: inherit;\n line-height: inherit;\n}\n\n::ng-deep .cell-link:hover {\n color: var(--mj-brand-primary);\n text-decoration: underline;\n}\n\n/* Email cells */\n::ng-deep .cell-email {\n font-family: inherit;\n font-size: 13px;\n}\n\n/* Foreign key link cells */\n::ng-deep .cell-fk-link {\n color: var(--grid-accent-color, var(--mj-brand-primary));\n text-decoration: none;\n cursor: pointer;\n transition: color 0.15s, text-decoration 0.15s;\n}\n\n::ng-deep .cell-fk-link:hover {\n color: var(--mj-brand-primary-hover);\n text-decoration: underline;\n}\n\n::ng-deep .cell-fk-link:active {\n color: var(--mj-brand-primary-active);\n}\n\n/* URL cells */\n::ng-deep .cell-url {\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n font-size: 13px;\n}\n\n/* Phone cells */\n::ng-deep .cell-phone {\n font-variant-numeric: tabular-nums;\n letter-spacing: 0.3px;\n}\n\n/* ========================================\n Skeleton Loading Animation\n ======================================== */\n\n@keyframes skeleton-shimmer {\n 0% {\n background-position: -200px 0;\n }\n 100% {\n background-position: calc(200px + 100%) 0;\n }\n}\n\n.skeleton-row {\n display: flex;\n height: 40px;\n align-items: center;\n padding: 0 12px;\n border-bottom: 1px solid var(--grid-cell-border-color, var(--mj-border-subtle));\n}\n\n.skeleton-cell {\n height: 16px;\n border-radius: 4px;\n background: linear-gradient(\n 90deg,\n var(--mj-bg-surface-sunken) 0px,\n var(--mj-bg-surface-hover) 40px,\n var(--mj-bg-surface-sunken) 80px\n );\n background-size: 200px 100%;\n animation: skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-cell-short {\n width: 60px;\n}\n\n.skeleton-cell-medium {\n width: 120px;\n}\n\n.skeleton-cell-long {\n width: 180px;\n}\n\n/* Selection checkbox column header */\n::ng-deep .ag-header-select-all {\n margin-right: 0;\n}\n\n/* ========================================\n Row Border Enhancement\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-row {\n border-bottom: 1px solid var(--grid-cell-border-color, var(--mj-border-subtle));\n}\n\n::ng-deep .ag-theme-alpine .ag-row:last-child {\n border-bottom: none;\n}\n\n/* ========================================\n Focus States\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-cell-focus {\n border: none !important;\n outline: none !important;\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:focus {\n outline: 2px solid var(--grid-accent-color, var(--mj-brand-primary));\n outline-offset: -2px;\n}\n\n/* ========================================\n Scrollbar Styling\n ======================================== */\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-track {\n background: var(--mj-bg-surface-card);\n border-radius: 4px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 4px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-thumb:hover {\n background: var(--mj-text-disabled);\n}\n\n/* ========================================\n Pinned Column Styling\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-pinned-left-cols-container {\n border-right: 2px solid var(--grid-border-color, var(--mj-border-default));\n}\n\n::ng-deep .ag-theme-alpine .ag-pinned-right-cols-container {\n border-left: 2px solid var(--grid-border-color, var(--mj-border-default));\n}\n\n/* ========================================\n Header Cell Enhancements\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-header-cell {\n font-weight: 600;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n color: var(--mj-text-secondary);\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:hover {\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell-sortable:hover .ag-header-cell-label {\n color: var(--grid-accent-color, var(--mj-brand-primary));\n}\n\n/* Sort icon animation */\n::ng-deep .ag-theme-alpine .ag-sort-indicator-icon {\n transition: transform 0.2s ease;\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:hover .ag-sort-indicator-icon {\n transform: scale(1.1);\n}\n\n/* ========================================\n Enhanced Multi-Sort Indicators\n These styles use :host to ensure they're scoped to this component\n and !important to override AG Grid's built-in styles\n ======================================== */\n\n/* Highlight sorted column headers with eye-catching background */\n/* Ascending = blue tint */\n:host ::ng-deep .ag-header-cell-sorted-asc {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface)) !important;\n position: relative;\n}\n\n/* Descending = pink tint */\n:host ::ng-deep .ag-header-cell-sorted-desc {\n background: color-mix(in srgb, var(--mj-status-error) 12%, var(--mj-bg-surface)) !important;\n position: relative;\n}\n\n/* Bottom border accent for sorted columns - ascending = blue */\n:host ::ng-deep .ag-header-cell-sorted-asc::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: var(--mj-brand-primary);\n}\n\n/* Bottom border accent for sorted columns - descending = pink */\n:host ::ng-deep .ag-header-cell-sorted-desc::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: var(--mj-status-error);\n}\n\n/* Sorted column header text - ascending = blue, bold */\n:host ::ng-deep .ag-header-cell-sorted-asc .ag-header-cell-text {\n color: var(--mj-brand-primary) !important;\n font-weight: 700 !important;\n}\n\n/* Sorted column header text - descending = pink, bold */\n:host ::ng-deep .ag-header-cell-sorted-desc .ag-header-cell-text {\n color: var(--mj-status-error) !important;\n font-weight: 700 !important;\n}\n\n/* Sort icons - larger and more prominent */\n/* Ascending = blue */\n:host ::ng-deep .ag-sort-ascending-icon {\n color: var(--mj-brand-primary) !important;\n}\n\n/* Descending = pink/magenta */\n:host ::ng-deep .ag-sort-descending-icon {\n color: var(--mj-status-error) !important;\n}\n\n:host ::ng-deep .ag-sort-ascending-icon .ag-icon,\n:host ::ng-deep .ag-sort-descending-icon .ag-icon {\n font-size: 14px !important;\n}\n\n/* Sort order number (1, 2, 3) - show as plain number, no bubble */\n/* This avoids AG Grid bug where .ag-sort-order is always present but empty when unsorted */\n/* The number is only visible when AG Grid populates it (i.e., when column is sorted) */\n/* Default blue for ascending */\n:host ::ng-deep .ag-sort-order {\n margin-left: 4px;\n font-size: 11px !important;\n font-weight: 700 !important;\n color: var(--mj-brand-primary) !important;\n}\n\n/* Sort order number - pink for descending */\n:host ::ng-deep .ag-header-cell-sorted-desc .ag-sort-order {\n color: var(--mj-status-error) !important;\n}\n\n/* Hover state for sortable headers */\n:host ::ng-deep .ag-header-cell-sortable:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n\n:host ::ng-deep .ag-header-cell-sortable:hover .ag-header-cell-text {\n color: var(--mj-brand-primary);\n}\n\n/* ========================================\n Aggregate Summary Row\n ======================================== */\n\n.mj-aggregate-summary {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n font-size: 13px;\n min-height: 44px;\n}\n\n.aggregate-summary-content {\n display: flex;\n flex-wrap: wrap;\n gap: 20px;\n align-items: center;\n}\n\n.aggregate-summary-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n}\n\n.aggregate-summary-item i {\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.agg-summary-label {\n color: var(--mj-text-muted);\n font-weight: 500;\n}\n\n.agg-summary-value {\n color: var(--mj-text-primary);\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n}\n\n.aggregate-loading {\n display: flex;\n align-items: center;\n color: var(--mj-text-disabled);\n}\n\n.aggregate-loading i {\n font-size: 14px;\n}\n\n/* ========================================\n AGGREGATE CARDS (displayed above grid)\n ======================================== */\n\n.mj-aggregate-cards {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.aggregate-card {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n min-width: 140px;\n transition: all 0.15s ease;\n}\n\n.aggregate-card:hover {\n border-color: var(--mj-border-strong);\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);\n}\n\n.aggregate-card-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n border-radius: 10px;\n color: var(--mj-brand-primary);\n font-size: 16px;\n flex-shrink: 0;\n}\n\n.aggregate-card-content {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.aggregate-card-label {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.aggregate-card-value {\n font-size: 18px;\n font-weight: 700;\n color: var(--mj-text-primary);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.aggregate-card-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 12px;\n color: var(--mj-text-disabled);\n}\n\n.aggregate-card-loading i {\n font-size: 16px;\n}\n\n/* ========================================\n Text Wrapping Styles\n ======================================== */\n\n/* Cell class for wrapped text */\n::ng-deep .cell-wrap-text {\n white-space: normal !important;\n word-wrap: break-word;\n line-height: 1.4;\n padding-top: 8px !important;\n padding-bottom: 8px !important;\n}\n\n/* Ensure AG Grid cells allow wrapping */\n::ng-deep .ag-cell.cell-wrap-text {\n overflow: visible;\n text-overflow: clip;\n}\n\n/* Maintain minimum row height when wrapping */\n::ng-deep .ag-row[style*=\"auto-height\"] {\n min-height: 40px;\n}\n\n/* ========================================\n Width filler column ([FillWidth])\n ========================================\n The trailing filler exists so row banding and horizontal rules reach the container's right edge\n instead of stopping wherever the last real column ends. For that to read as \"the table extends\"\n rather than \"there is an empty column here\", it must lose every affordance that makes a column\n look like a column: its own vertical rule, the header hover/menu, and the resize handle. What is\n left is bare row background \u2014 which is exactly the point. */\n::ng-deep .ag-header-cell.mj-grid-filler-header,\n::ng-deep .ag-cell.mj-grid-filler-cell {\n border-left: none !important;\n border-right: none !important;\n cursor: default;\n}\n\n/* The hover tint is OURS (see \".ag-theme-alpine .ag-header-cell:hover\" above), not AG Grid's, so it\n has to be switched off here or the filler lights up as though it were interactive. Everything else\n the filler needs suppressed \u2014 resize grip, header menu, moving, tool-panel entry \u2014 is handled by\n native colDef flags on the column itself, not by CSS. */\n::ng-deep .ag-header-cell.mj-grid-filler-header:hover {\n background-color: transparent !important;\n}\n\n/* AG Grid injects cellRenderer HTML without Angular attributes, so these\n must pierce encapsulation or the image renders at intrinsic SVG size. */\n::ng-deep .ag-cell.mj-grid-image-cell {\n display: flex !important;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n padding: 4px !important;\n}\n::ng-deep .ag-header-cell.mj-grid-image-header .ag-header-cell-label {\n justify-content: center;\n}\n::ng-deep .cell-image-link {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n overflow: hidden;\n border-radius: 50%;\n background: var(--mj-bg-surface-sunken);\n flex-shrink: 0;\n}\n::ng-deep .cell-image {\n width: 100%;\n height: 100%;\n max-width: 28px;\n max-height: 28px;\n object-fit: cover;\n object-position: center;\n display: block;\n}\n"] }]
5071
5370
  }], () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i1.ExportService }, { type: i0.NgZone }], { Params: [{
5072
5371
  type: Input
5073
5372
  }], AllowLoad: [{
@@ -5310,9 +5609,9 @@ export class EntityDataGridComponent extends BaseAngularComponent {
5310
5609
  type: Output
5311
5610
  }], EntityActionRequested: [{
5312
5611
  type: Output
5313
- }], gridContainer: [{
5612
+ }], GridContainer: [{
5314
5613
  type: ViewChild,
5315
5614
  args: ['gridContainer']
5316
5615
  }] }); })();
5317
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityDataGridComponent, { className: "EntityDataGridComponent", filePath: "src/lib/entity-data-grid/entity-data-grid.component.ts", lineNumber: 191 }); })();
5616
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityDataGridComponent, { className: "EntityDataGridComponent", filePath: "src/lib/entity-data-grid/entity-data-grid.component.ts", lineNumber: 194 }); })();
5318
5617
  //# sourceMappingURL=entity-data-grid.component.js.map