@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
@@ -867,11 +867,11 @@ export class ViewSelectorComponent extends BaseAngularComponent {
867
867
  i0.ɵɵconditional(ctx.IsDropdownOpen ? 9 : -1);
868
868
  i0.ɵɵadvance();
869
869
  i0.ɵɵconditional(ctx.SelectedViewID ? 10 : -1);
870
- } }, dependencies: [i1.DefaultValueAccessor, i1.NgControlStatus, i1.NgModel, i2.MJEmptyStateComponent], styles: ["/* ========================================\n VIEW SELECTOR - All classes prefixed vs-\n to avoid conflicts (ViewEncapsulation.None)\n ======================================== */\n\n/* Container */\n.vs-container {\n display: flex;\n align-items: center;\n gap: 4px;\n position: relative;\n}\n\n/* Backdrop for closing panel */\n.vs-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 999;\n background: transparent;\n}\n\n.vs-wrapper {\n position: relative;\n}\n\n/* Main Button */\n.vs-button {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n transition: all 0.15s ease;\n min-width: 140px;\n max-width: 280px;\n}\n\n.vs-button:hover {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.vs-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.vs-button.open {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.vs-button.modified {\n border-color: var(--mj-status-warning);\n}\n\n.vs-btn-icon {\n color: var(--mj-brand-primary);\n font-size: 12px;\n}\n\n.vs-btn-name {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n text-align: left;\n}\n\n.vs-modified-dot {\n color: var(--mj-status-warning);\n font-weight: bold;\n font-size: 18px;\n line-height: 1;\n}\n\n.vs-arrow {\n color: var(--mj-text-muted);\n font-size: 10px;\n transition: transform 0.2s ease;\n}\n\n.vs-arrow.rotated {\n transform: rotate(180deg);\n}\n\n/* ========================================\n RICH VIEW PANEL\n Styles ported directly from prototype\n ======================================== */\n\n/* Reset inherited styles inside panel */\n.vs-panel,\n.vs-panel * {\n box-sizing: border-box;\n line-height: 1.5;\n text-align: left;\n}\n\n.vs-panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n width: 400px;\n max-width: calc(100vw - 40px);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16);\n z-index: 1000;\n overflow: hidden;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n/* Panel Header */\n.vs-panel-header {\n padding: 16px 20px;\n display: flex;\n align-items: center;\n gap: 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.vs-header-icon {\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n.vs-panel-title {\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.vs-entity-badge {\n font-size: 11px;\n font-weight: 500;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.vs-close-btn {\n margin-left: auto;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 16px;\n padding: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.vs-close-btn:hover {\n color: var(--mj-text-primary);\n}\n\n/* Panel Search */\n.vs-panel-search {\n padding: 12px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.vs-search-input {\n width: 100%;\n padding: 6px 12px 6px 32px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-size: 13px;\n background: var(--mj-bg-surface-card) url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512' fill='%23999'%3E%3Cpath d='M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208z'/%3E%3C/svg%3E\") no-repeat 10px center;\n background-size: 14px;\n box-sizing: border-box;\n font-family: inherit;\n}\n\n.vs-search-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n}\n\n.vs-search-input::placeholder {\n color: var(--mj-text-muted);\n}\n\n/* Panel Body */\n.vs-panel-body {\n max-height: 400px;\n overflow-y: auto;\n}\n\n/* View Section */\n.vs-section {\n padding: 8px 0;\n}\n\n.vs-section-header {\n padding: 6px 20px;\n font-size: 11px;\n font-weight: 700;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.vs-section-header i {\n font-size: 10px;\n}\n\n.vs-section-count {\n background: var(--mj-border-default);\n padding: 0 6px;\n border-radius: 8px;\n font-size: 10px;\n}\n\n/* View Card */\n.vs-card {\n padding: 10px 20px;\n cursor: pointer;\n transition: background 0.1s;\n display: flex;\n align-items: flex-start;\n gap: 12px;\n}\n\n.vs-card:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.vs-card.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n border-left: 3px solid var(--mj-brand-primary);\n padding-left: 17px;\n}\n\n/* View Card Icon */\n.vs-card-icon {\n width: 32px;\n height: 32px;\n border-radius: 6px;\n background: var(--mj-bg-surface-card);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n font-size: 14px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n.vs-card-icon.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.vs-card-icon.shared {\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-muted);\n}\n\n/* View Card Content */\n.vs-card-content {\n flex: 1;\n min-width: 0;\n padding-top: 4px;\n}\n\n.vs-card-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.vs-card.active .vs-card-name {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.vs-default-badge {\n font-size: 9px;\n font-weight: 600;\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n padding: 1px 5px;\n border-radius: 6px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.vs-shared-badge {\n font-size: 9px;\n font-weight: 600;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n padding: 1px 5px;\n border-radius: 6px;\n}\n\n.vs-shared-badge.editable {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.vs-card-desc {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.vs-card-meta {\n display: flex;\n gap: 10px;\n margin-top: 4px;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.vs-card-meta span {\n display: flex;\n align-items: center;\n gap: 3px;\n}\n\n.vs-card-meta i {\n font-size: 10px;\n}\n\n/* View Card Check */\n.vs-card-check {\n color: var(--mj-brand-primary);\n font-size: 12px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n/* View Card Actions (hover reveal) */\n.vs-card-actions {\n display: flex;\n gap: 2px;\n opacity: 0;\n transition: opacity 0.15s;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n.vs-card:hover .vs-card-actions {\n opacity: 1;\n}\n\n.vs-action-btn {\n width: 26px;\n height: 26px;\n border-radius: 4px;\n border: none;\n background: none;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.vs-action-btn:hover {\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-primary);\n}\n\n/* Loading */\n.vs-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 20px;\n color: var(--mj-text-muted);\n font-size: 13px;\n text-align: center;\n}\n\n/* Panel Footer */\n.vs-panel-footer {\n padding: 12px 20px;\n border-top: 1px solid var(--mj-border-default);\n display: flex;\n gap: 8px;\n}\n\n.vs-panel-btn {\n flex: 1;\n padding: 8px 12px;\n border-radius: 4px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n cursor: pointer;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n transition: all 0.15s;\n font-family: inherit;\n text-align: center;\n}\n\n.vs-panel-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.vs-panel-btn.primary {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n.vs-panel-btn.primary:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.vs-panel-btn i {\n font-size: 11px;\n}\n\n/* ========================================\n ICON BUTTONS (outside panel)\n ======================================== */\n\n.vs-icon-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n cursor: pointer;\n color: var(--mj-text-secondary);\n transition: all 0.15s ease;\n}\n\n.vs-icon-btn:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n color: var(--mj-brand-primary);\n}\n\n.vs-icon-btn i {\n font-size: 12px;\n}\n\n.vs-export-btn:hover {\n color: var(--mj-status-success);\n}\n\n/* Responsive */\n@media (max-width: 768px) {\n .vs-button {\n min-width: 100px;\n max-width: 180px;\n padding: 6px 10px;\n }\n\n .vs-panel {\n width: calc(100vw - 32px);\n max-width: 400px;\n }\n}\n"], encapsulation: 2 });
870
+ } }, dependencies: [i1.DefaultValueAccessor, i1.NgControlStatus, i1.NgModel, i2.MJEmptyStateComponent], styles: ["/* ========================================\n VIEW SELECTOR - All classes prefixed vs-\n to avoid conflicts (ViewEncapsulation.None)\n ======================================== */\n\n/* Container */\n.vs-container {\n display: flex;\n align-items: center;\n gap: 4px;\n position: relative;\n}\n\n/* Backdrop for closing panel */\n.vs-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 999;\n background: transparent;\n}\n\n.vs-wrapper {\n position: relative;\n}\n\n/* Main Button */\n.vs-button {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n transition: all 0.15s ease;\n min-width: 140px;\n max-width: 280px;\n}\n\n.vs-button:hover {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.vs-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.vs-button.open {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.vs-button.modified {\n border-color: var(--mj-status-warning);\n}\n\n.vs-btn-icon {\n color: var(--mj-brand-primary);\n font-size: 12px;\n}\n\n.vs-btn-name {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n text-align: left;\n}\n\n.vs-modified-dot {\n color: var(--mj-status-warning);\n font-weight: bold;\n font-size: 18px;\n line-height: 1;\n}\n\n.vs-arrow {\n color: var(--mj-text-muted);\n font-size: 10px;\n transition: transform 0.2s ease;\n}\n\n.vs-arrow.rotated {\n transform: rotate(180deg);\n}\n\n/* ========================================\n RICH VIEW PANEL\n Styles ported directly from prototype\n ======================================== */\n\n/* Reset inherited styles inside panel */\n.vs-panel,\n.vs-panel * {\n box-sizing: border-box;\n line-height: 1.5;\n text-align: left;\n}\n\n.vs-panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n width: 400px;\n max-width: calc(100vw - 40px);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16);\n z-index: 1000;\n overflow: hidden;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n/* Panel Header */\n.vs-panel-header {\n padding: 16px 20px;\n display: flex;\n align-items: center;\n gap: 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.vs-header-icon {\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n.vs-panel-title {\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.vs-entity-badge {\n font-size: 11px;\n font-weight: 500;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.vs-close-btn {\n margin-left: auto;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 16px;\n padding: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.vs-close-btn:hover {\n color: var(--mj-text-primary);\n}\n\n/* Panel Search */\n.vs-panel-search {\n padding: 12px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.vs-search-input {\n width: 100%;\n padding: 6px 12px 6px 32px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-size: 13px;\n background: var(--mj-bg-surface-card) url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512' fill='%23999'%3E%3Cpath d='M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208z'/%3E%3C/svg%3E\") no-repeat 10px center;\n background-size: 14px;\n box-sizing: border-box;\n font-family: inherit;\n}\n\n.vs-search-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n}\n\n.vs-search-input::placeholder {\n color: var(--mj-text-muted);\n}\n\n/* Panel Body */\n.vs-panel-body {\n max-height: 400px;\n overflow-y: auto;\n}\n\n/* View Section */\n.vs-section {\n padding: 8px 0;\n}\n\n.vs-section-header {\n padding: 6px 20px;\n font-size: 11px;\n font-weight: 700;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.vs-section-header i {\n font-size: 10px;\n}\n\n.vs-section-count {\n background: var(--mj-border-default);\n padding: 0 6px;\n border-radius: 8px;\n font-size: 10px;\n}\n\n/* View Card */\n.vs-card {\n padding: 10px 20px;\n cursor: pointer;\n transition: background 0.1s;\n display: flex;\n align-items: flex-start;\n gap: 12px;\n}\n\n.vs-card:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.vs-card.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n border-left: 3px solid var(--mj-brand-primary);\n padding-left: 17px;\n}\n\n/* View Card Icon */\n.vs-card-icon {\n width: 32px;\n height: 32px;\n border-radius: 6px;\n background: var(--mj-bg-surface-card);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n font-size: 14px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n.vs-card-icon.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.vs-card-icon.shared {\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-muted);\n}\n\n/* View Card Content */\n.vs-card-content {\n flex: 1;\n min-width: 0;\n padding-top: 4px;\n}\n\n.vs-card-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.vs-card.active .vs-card-name {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.vs-default-badge {\n font-size: 9px;\n font-weight: 600;\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n padding: 1px 5px;\n border-radius: 6px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.vs-shared-badge {\n font-size: 9px;\n font-weight: 600;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n padding: 1px 5px;\n border-radius: 6px;\n}\n\n.vs-shared-badge.editable {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.vs-card-desc {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.vs-card-meta {\n display: flex;\n gap: 10px;\n margin-top: 4px;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.vs-card-meta span {\n display: flex;\n align-items: center;\n gap: 3px;\n}\n\n.vs-card-meta i {\n font-size: 10px;\n}\n\n/* View Card Check */\n.vs-card-check {\n color: var(--mj-brand-primary);\n font-size: 12px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n/* View Card Actions (hover reveal) */\n.vs-card-actions {\n display: flex;\n gap: 2px;\n opacity: 0;\n transition: opacity 0.15s;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n/* :focus-within matters as much as :hover \u2014 these buttons (Duplicate, Configure,\n Open in Tab) were reachable ONLY by pointer. A keyboard user could tab into them\n and see nothing at opacity 0, and anything driving the page without a hover\n (assistive tech, automation) could not reach them at all. */\n.vs-card:hover .vs-card-actions,\n.vs-card:focus-within .vs-card-actions {\n opacity: 1;\n}\n\n.vs-action-btn {\n width: 26px;\n height: 26px;\n border-radius: 4px;\n border: none;\n background: none;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.vs-action-btn:hover {\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-primary);\n}\n\n/* Loading */\n.vs-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 20px;\n color: var(--mj-text-muted);\n font-size: 13px;\n text-align: center;\n}\n\n/* Panel Footer */\n.vs-panel-footer {\n padding: 12px 20px;\n border-top: 1px solid var(--mj-border-default);\n display: flex;\n gap: 8px;\n}\n\n.vs-panel-btn {\n flex: 1;\n padding: 8px 12px;\n border-radius: 4px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n cursor: pointer;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n transition: all 0.15s;\n font-family: inherit;\n text-align: center;\n}\n\n.vs-panel-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.vs-panel-btn.primary {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n.vs-panel-btn.primary:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.vs-panel-btn i {\n font-size: 11px;\n}\n\n/* ========================================\n ICON BUTTONS (outside panel)\n ======================================== */\n\n.vs-icon-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n cursor: pointer;\n color: var(--mj-text-secondary);\n transition: all 0.15s ease;\n}\n\n.vs-icon-btn:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n color: var(--mj-brand-primary);\n}\n\n.vs-icon-btn i {\n font-size: 12px;\n}\n\n.vs-export-btn:hover {\n color: var(--mj-status-success);\n}\n\n/* Responsive */\n@media (max-width: 768px) {\n .vs-button {\n min-width: 100px;\n max-width: 180px;\n padding: 6px 10px;\n }\n\n .vs-panel {\n width: calc(100vw - 32px);\n max-width: 400px;\n }\n}\n"], encapsulation: 2 });
871
871
  }
872
872
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ViewSelectorComponent, [{
873
873
  type: Component,
874
- args: [{ standalone: false, selector: 'mj-view-selector', encapsulation: ViewEncapsulation.None, template: "<div class=\"vs-container\">\n <!-- Backdrop for closing panel -->\n @if (IsDropdownOpen) {\n <div class=\"vs-backdrop\" (click)=\"CloseDropdown()\"></div>\n }\n\n <!-- Main View Selector Button -->\n <div class=\"vs-wrapper\">\n <button\n class=\"vs-button\"\n [class.modified]=\"ViewModified\"\n [class.open]=\"IsDropdownOpen\"\n (click)=\"ToggleDropdown()\"\n [disabled]=\"IsLoading\"\n title=\"Select a saved view\">\n <i class=\"fa-solid fa-layer-group vs-btn-icon\"></i>\n <span class=\"vs-btn-name\">{{ DisplayName }}</span>\n @if (ViewModified) {\n <span class=\"vs-modified-dot\" title=\"View has unsaved changes\">\u2022</span>\n }\n <i class=\"fa-solid fa-chevron-down vs-arrow\" [class.rotated]=\"IsDropdownOpen\"></i>\n </button>\n\n <!-- Rich View Panel -->\n @if (IsDropdownOpen) {\n <div class=\"vs-panel\">\n <!-- Panel Header -->\n <div class=\"vs-panel-header\">\n <i class=\"fa-solid fa-table-list vs-header-icon\"></i>\n <span class=\"vs-panel-title\">Views</span>\n @if (Entity) {\n <span class=\"vs-Entity-badge\">{{ Entity.DisplayNameOrName }}</span>\n }\n <button class=\"vs-close-btn\" (click)=\"CloseDropdown()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Search -->\n <div class=\"vs-panel-search\">\n <input\n type=\"text\"\n class=\"vs-search-input\"\n placeholder=\"Search views...\"\n [(ngModel)]=\"SearchText\"\n (keydown.escape)=\"SearchText = ''\"\n />\n </div>\n\n <!-- Panel Body -->\n <div class=\"vs-panel-body\">\n <!-- Default Option -->\n <div class=\"vs-section\">\n <div\n class=\"vs-card\"\n [class.active]=\"!SelectedViewID\"\n (click)=\"SelectDefault()\">\n <div class=\"vs-card-icon\" [class.active]=\"!SelectedViewID\">\n <i class=\"fa-solid fa-table\"></i>\n </div>\n <div class=\"vs-card-content\">\n <div class=\"vs-card-name\">(Default)</div>\n </div>\n @if (!SelectedViewID) {\n <i class=\"fa-solid fa-check vs-card-check\"></i>\n }\n </div>\n </div>\n\n <!-- My Views Section -->\n @if (FilteredMyViews.length > 0) {\n <div class=\"vs-section\">\n <div class=\"vs-section-header\">\n <i class=\"fa-solid fa-user\"></i>\n <span>My Views</span>\n <span class=\"vs-section-count\">{{ FilteredMyViews.length }}</span>\n </div>\n @for (view of FilteredMyViews; track view.id) {\n <div\n class=\"vs-card\"\n [class.active]=\"SelectedViewID === view.id\"\n (click)=\"SelectView(view)\">\n <div class=\"vs-card-icon\" [class.active]=\"SelectedViewID === view.id\">\n <i class=\"fa-solid fa-table-list\"></i>\n </div>\n <div class=\"vs-card-content\">\n <div class=\"vs-card-name\">\n {{ view.name }}\n @if (view.isDefault) {\n <span class=\"vs-default-badge\">Default</span>\n }\n </div>\n @if (view.entity.Description) {\n <div class=\"vs-card-desc\">{{ view.entity.Description }}</div>\n }\n @if (GetViewFilterCount(view) > 0 || GetViewColumnCount(view) > 0 || GetViewSortInfo(view)) {\n <div class=\"vs-card-meta\">\n @if (GetViewFilterCount(view) > 0) {\n <span><i class=\"fa-solid fa-filter\"></i> {{ GetViewFilterCount(view) }} filter{{ GetViewFilterCount(view) !== 1 ? 's' : '' }}</span>\n }\n @if (GetViewColumnCount(view) > 0) {\n <span><i class=\"fa-solid fa-table-columns\"></i> {{ GetViewColumnCount(view) }} col{{ GetViewColumnCount(view) !== 1 ? 's' : '' }}</span>\n }\n @if (GetViewSortInfo(view)) {\n <span><i class=\"fa-solid fa-arrow-down-short-wide\"></i> {{ GetViewSortInfo(view) }}</span>\n }\n </div>\n }\n </div>\n <div class=\"vs-card-actions\">\n <button\n class=\"vs-action-btn\"\n (click)=\"OnDuplicateView(view.id, $event)\"\n title=\"Duplicate\">\n <i class=\"fa-regular fa-copy\"></i>\n </button>\n <button\n class=\"vs-action-btn\"\n (click)=\"OnConfigureViewById(view.id, $event)\"\n title=\"Configure\">\n <i class=\"fa-solid fa-sliders\"></i>\n </button>\n <button\n class=\"vs-action-btn\"\n (click)=\"OnOpenViewInTab(view.id, $event)\"\n title=\"Open in Tab\">\n <i class=\"fa-solid fa-up-right-from-square\"></i>\n </button>\n </div>\n @if (SelectedViewID === view.id) {\n <i class=\"fa-solid fa-check vs-card-check\"></i>\n }\n </div>\n }\n </div>\n }\n\n <!-- Shared Views Section -->\n @if (FilteredSharedViews.length > 0) {\n <div class=\"vs-section\">\n <div class=\"vs-section-header\">\n <i class=\"fa-solid fa-share-nodes\"></i>\n <span>Shared With Me</span>\n <span class=\"vs-section-count\">{{ FilteredSharedViews.length }}</span>\n </div>\n @for (view of FilteredSharedViews; track view.id) {\n <div\n class=\"vs-card\"\n [class.active]=\"SelectedViewID === view.id\"\n (click)=\"SelectView(view)\">\n <div class=\"vs-card-icon shared\">\n <i class=\"fa-solid fa-users\"></i>\n </div>\n <div class=\"vs-card-content\">\n <div class=\"vs-card-name\">\n {{ view.name }}\n @if (!view.userCanEdit) {\n <span class=\"vs-shared-badge\">View Only</span>\n } @else {\n <span class=\"vs-shared-badge editable\">Can Edit</span>\n }\n </div>\n @if (view.entity.Description) {\n <div class=\"vs-card-desc\">{{ view.entity.Description }}</div>\n }\n @if (GetViewFilterCount(view) > 0 || GetViewColumnCount(view) > 0) {\n <div class=\"vs-card-meta\">\n @if (GetViewFilterCount(view) > 0) {\n <span><i class=\"fa-solid fa-filter\"></i> {{ GetViewFilterCount(view) }} filter{{ GetViewFilterCount(view) !== 1 ? 's' : '' }}</span>\n }\n @if (GetViewColumnCount(view) > 0) {\n <span><i class=\"fa-solid fa-table-columns\"></i> {{ GetViewColumnCount(view) }} col{{ GetViewColumnCount(view) !== 1 ? 's' : '' }}</span>\n }\n </div>\n }\n </div>\n <div class=\"vs-card-actions\">\n <button\n class=\"vs-action-btn\"\n (click)=\"OnDuplicateView(view.id, $event)\"\n title=\"Duplicate as My View\">\n <i class=\"fa-regular fa-copy\"></i>\n </button>\n @if (view.userCanEdit) {\n <button\n class=\"vs-action-btn\"\n (click)=\"OnConfigureViewById(view.id, $event)\"\n title=\"Configure\">\n <i class=\"fa-solid fa-sliders\"></i>\n </button>\n }\n </div>\n @if (SelectedViewID === view.id) {\n <i class=\"fa-solid fa-check vs-card-check\"></i>\n }\n </div>\n }\n </div>\n }\n\n <!-- No Views Message -->\n @if (!HasViews && !IsLoading) {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-bookmark\"\n Title=\"No saved views yet\"\n Message=\"Save your current configuration as a view\" />\n }\n\n <!-- No Search Results -->\n @if (HasViews && FilteredMyViews.length === 0 && FilteredSharedViews.length === 0 && SearchText) {\n <mj-empty-state\n Size=\"compact\"\n Variant=\"no-results\"\n [Title]=\"NoViewsMatchMessage\" />\n }\n\n <!-- Loading Indicator -->\n @if (IsLoading) {\n <div class=\"vs-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading views...</span>\n </div>\n }\n </div>\n\n <!-- Panel Footer -->\n <div class=\"vs-panel-footer\">\n <button\n class=\"vs-panel-btn primary\"\n (click)=\"OnQuickSave()\"\n title=\"Save current view configuration\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>New View</span>\n </button>\n <button\n class=\"vs-panel-btn\"\n (click)=\"OnResetToDefault()\"\n title=\"Reset to default view\">\n <i class=\"fa-solid fa-arrow-rotate-left\"></i>\n <span>Reset to Default</span>\n </button>\n </div>\n </div>\n }\n </div>\n\n <!-- Open in Tab Button (only visible when a view is selected) -->\n @if (SelectedViewID) {\n <button\n class=\"vs-icon-btn\"\n (click)=\"OnOpenInTab()\"\n title=\"Open this view in a new tab\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n </button>\n }\n\n <!-- Configure View Button -->\n <button\n class=\"vs-icon-btn\"\n (click)=\"OnConfigureView()\"\n title=\"Configure view settings (columns, filters, sorting)\">\n <i class=\"fa-solid fa-sliders-h\"></i>\n </button>\n\n <!-- Create New Record Button -->\n <button\n class=\"vs-icon-btn\"\n (click)=\"OnCreateNewRecord()\"\n title=\"Create new record\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n\n <!-- Export Button -->\n <button\n class=\"vs-icon-btn vs-export-btn\"\n (click)=\"OnExport()\"\n title=\"Export\">\n <i class=\"fa-solid fa-file-excel\"></i>\n </button>\n</div>\n", styles: ["/* ========================================\n VIEW SELECTOR - All classes prefixed vs-\n to avoid conflicts (ViewEncapsulation.None)\n ======================================== */\n\n/* Container */\n.vs-container {\n display: flex;\n align-items: center;\n gap: 4px;\n position: relative;\n}\n\n/* Backdrop for closing panel */\n.vs-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 999;\n background: transparent;\n}\n\n.vs-wrapper {\n position: relative;\n}\n\n/* Main Button */\n.vs-button {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n transition: all 0.15s ease;\n min-width: 140px;\n max-width: 280px;\n}\n\n.vs-button:hover {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.vs-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.vs-button.open {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.vs-button.modified {\n border-color: var(--mj-status-warning);\n}\n\n.vs-btn-icon {\n color: var(--mj-brand-primary);\n font-size: 12px;\n}\n\n.vs-btn-name {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n text-align: left;\n}\n\n.vs-modified-dot {\n color: var(--mj-status-warning);\n font-weight: bold;\n font-size: 18px;\n line-height: 1;\n}\n\n.vs-arrow {\n color: var(--mj-text-muted);\n font-size: 10px;\n transition: transform 0.2s ease;\n}\n\n.vs-arrow.rotated {\n transform: rotate(180deg);\n}\n\n/* ========================================\n RICH VIEW PANEL\n Styles ported directly from prototype\n ======================================== */\n\n/* Reset inherited styles inside panel */\n.vs-panel,\n.vs-panel * {\n box-sizing: border-box;\n line-height: 1.5;\n text-align: left;\n}\n\n.vs-panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n width: 400px;\n max-width: calc(100vw - 40px);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16);\n z-index: 1000;\n overflow: hidden;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n/* Panel Header */\n.vs-panel-header {\n padding: 16px 20px;\n display: flex;\n align-items: center;\n gap: 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.vs-header-icon {\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n.vs-panel-title {\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.vs-entity-badge {\n font-size: 11px;\n font-weight: 500;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.vs-close-btn {\n margin-left: auto;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 16px;\n padding: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.vs-close-btn:hover {\n color: var(--mj-text-primary);\n}\n\n/* Panel Search */\n.vs-panel-search {\n padding: 12px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.vs-search-input {\n width: 100%;\n padding: 6px 12px 6px 32px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-size: 13px;\n background: var(--mj-bg-surface-card) url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512' fill='%23999'%3E%3Cpath d='M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208z'/%3E%3C/svg%3E\") no-repeat 10px center;\n background-size: 14px;\n box-sizing: border-box;\n font-family: inherit;\n}\n\n.vs-search-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n}\n\n.vs-search-input::placeholder {\n color: var(--mj-text-muted);\n}\n\n/* Panel Body */\n.vs-panel-body {\n max-height: 400px;\n overflow-y: auto;\n}\n\n/* View Section */\n.vs-section {\n padding: 8px 0;\n}\n\n.vs-section-header {\n padding: 6px 20px;\n font-size: 11px;\n font-weight: 700;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.vs-section-header i {\n font-size: 10px;\n}\n\n.vs-section-count {\n background: var(--mj-border-default);\n padding: 0 6px;\n border-radius: 8px;\n font-size: 10px;\n}\n\n/* View Card */\n.vs-card {\n padding: 10px 20px;\n cursor: pointer;\n transition: background 0.1s;\n display: flex;\n align-items: flex-start;\n gap: 12px;\n}\n\n.vs-card:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.vs-card.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n border-left: 3px solid var(--mj-brand-primary);\n padding-left: 17px;\n}\n\n/* View Card Icon */\n.vs-card-icon {\n width: 32px;\n height: 32px;\n border-radius: 6px;\n background: var(--mj-bg-surface-card);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n font-size: 14px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n.vs-card-icon.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.vs-card-icon.shared {\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-muted);\n}\n\n/* View Card Content */\n.vs-card-content {\n flex: 1;\n min-width: 0;\n padding-top: 4px;\n}\n\n.vs-card-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.vs-card.active .vs-card-name {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.vs-default-badge {\n font-size: 9px;\n font-weight: 600;\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n padding: 1px 5px;\n border-radius: 6px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.vs-shared-badge {\n font-size: 9px;\n font-weight: 600;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n padding: 1px 5px;\n border-radius: 6px;\n}\n\n.vs-shared-badge.editable {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.vs-card-desc {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.vs-card-meta {\n display: flex;\n gap: 10px;\n margin-top: 4px;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.vs-card-meta span {\n display: flex;\n align-items: center;\n gap: 3px;\n}\n\n.vs-card-meta i {\n font-size: 10px;\n}\n\n/* View Card Check */\n.vs-card-check {\n color: var(--mj-brand-primary);\n font-size: 12px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n/* View Card Actions (hover reveal) */\n.vs-card-actions {\n display: flex;\n gap: 2px;\n opacity: 0;\n transition: opacity 0.15s;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n.vs-card:hover .vs-card-actions {\n opacity: 1;\n}\n\n.vs-action-btn {\n width: 26px;\n height: 26px;\n border-radius: 4px;\n border: none;\n background: none;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.vs-action-btn:hover {\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-primary);\n}\n\n/* Loading */\n.vs-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 20px;\n color: var(--mj-text-muted);\n font-size: 13px;\n text-align: center;\n}\n\n/* Panel Footer */\n.vs-panel-footer {\n padding: 12px 20px;\n border-top: 1px solid var(--mj-border-default);\n display: flex;\n gap: 8px;\n}\n\n.vs-panel-btn {\n flex: 1;\n padding: 8px 12px;\n border-radius: 4px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n cursor: pointer;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n transition: all 0.15s;\n font-family: inherit;\n text-align: center;\n}\n\n.vs-panel-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.vs-panel-btn.primary {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n.vs-panel-btn.primary:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.vs-panel-btn i {\n font-size: 11px;\n}\n\n/* ========================================\n ICON BUTTONS (outside panel)\n ======================================== */\n\n.vs-icon-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n cursor: pointer;\n color: var(--mj-text-secondary);\n transition: all 0.15s ease;\n}\n\n.vs-icon-btn:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n color: var(--mj-brand-primary);\n}\n\n.vs-icon-btn i {\n font-size: 12px;\n}\n\n.vs-export-btn:hover {\n color: var(--mj-status-success);\n}\n\n/* Responsive */\n@media (max-width: 768px) {\n .vs-button {\n min-width: 100px;\n max-width: 180px;\n padding: 6px 10px;\n }\n\n .vs-panel {\n width: calc(100vw - 32px);\n max-width: 400px;\n }\n}\n"] }]
874
+ args: [{ standalone: false, selector: 'mj-view-selector', encapsulation: ViewEncapsulation.None, template: "<div class=\"vs-container\">\n <!-- Backdrop for closing panel -->\n @if (IsDropdownOpen) {\n <div class=\"vs-backdrop\" (click)=\"CloseDropdown()\"></div>\n }\n\n <!-- Main View Selector Button -->\n <div class=\"vs-wrapper\">\n <button\n class=\"vs-button\"\n [class.modified]=\"ViewModified\"\n [class.open]=\"IsDropdownOpen\"\n (click)=\"ToggleDropdown()\"\n [disabled]=\"IsLoading\"\n title=\"Select a saved view\">\n <i class=\"fa-solid fa-layer-group vs-btn-icon\"></i>\n <span class=\"vs-btn-name\">{{ DisplayName }}</span>\n @if (ViewModified) {\n <span class=\"vs-modified-dot\" title=\"View has unsaved changes\">\u2022</span>\n }\n <i class=\"fa-solid fa-chevron-down vs-arrow\" [class.rotated]=\"IsDropdownOpen\"></i>\n </button>\n\n <!-- Rich View Panel -->\n @if (IsDropdownOpen) {\n <div class=\"vs-panel\">\n <!-- Panel Header -->\n <div class=\"vs-panel-header\">\n <i class=\"fa-solid fa-table-list vs-header-icon\"></i>\n <span class=\"vs-panel-title\">Views</span>\n @if (Entity) {\n <span class=\"vs-Entity-badge\">{{ Entity.DisplayNameOrName }}</span>\n }\n <button class=\"vs-close-btn\" (click)=\"CloseDropdown()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Search -->\n <div class=\"vs-panel-search\">\n <input\n type=\"text\"\n class=\"vs-search-input\"\n placeholder=\"Search views...\"\n [(ngModel)]=\"SearchText\"\n (keydown.escape)=\"SearchText = ''\"\n />\n </div>\n\n <!-- Panel Body -->\n <div class=\"vs-panel-body\">\n <!-- Default Option -->\n <div class=\"vs-section\">\n <div\n class=\"vs-card\"\n [class.active]=\"!SelectedViewID\"\n (click)=\"SelectDefault()\">\n <div class=\"vs-card-icon\" [class.active]=\"!SelectedViewID\">\n <i class=\"fa-solid fa-table\"></i>\n </div>\n <div class=\"vs-card-content\">\n <div class=\"vs-card-name\">(Default)</div>\n </div>\n @if (!SelectedViewID) {\n <i class=\"fa-solid fa-check vs-card-check\"></i>\n }\n </div>\n </div>\n\n <!-- My Views Section -->\n @if (FilteredMyViews.length > 0) {\n <div class=\"vs-section\">\n <div class=\"vs-section-header\">\n <i class=\"fa-solid fa-user\"></i>\n <span>My Views</span>\n <span class=\"vs-section-count\">{{ FilteredMyViews.length }}</span>\n </div>\n @for (view of FilteredMyViews; track view.id) {\n <div\n class=\"vs-card\"\n [class.active]=\"SelectedViewID === view.id\"\n (click)=\"SelectView(view)\">\n <div class=\"vs-card-icon\" [class.active]=\"SelectedViewID === view.id\">\n <i class=\"fa-solid fa-table-list\"></i>\n </div>\n <div class=\"vs-card-content\">\n <div class=\"vs-card-name\">\n {{ view.name }}\n @if (view.isDefault) {\n <span class=\"vs-default-badge\">Default</span>\n }\n </div>\n @if (view.entity.Description) {\n <div class=\"vs-card-desc\">{{ view.entity.Description }}</div>\n }\n @if (GetViewFilterCount(view) > 0 || GetViewColumnCount(view) > 0 || GetViewSortInfo(view)) {\n <div class=\"vs-card-meta\">\n @if (GetViewFilterCount(view) > 0) {\n <span><i class=\"fa-solid fa-filter\"></i> {{ GetViewFilterCount(view) }} filter{{ GetViewFilterCount(view) !== 1 ? 's' : '' }}</span>\n }\n @if (GetViewColumnCount(view) > 0) {\n <span><i class=\"fa-solid fa-table-columns\"></i> {{ GetViewColumnCount(view) }} col{{ GetViewColumnCount(view) !== 1 ? 's' : '' }}</span>\n }\n @if (GetViewSortInfo(view)) {\n <span><i class=\"fa-solid fa-arrow-down-short-wide\"></i> {{ GetViewSortInfo(view) }}</span>\n }\n </div>\n }\n </div>\n <div class=\"vs-card-actions\">\n <button\n class=\"vs-action-btn\"\n (click)=\"OnDuplicateView(view.id, $event)\"\n title=\"Duplicate\">\n <i class=\"fa-regular fa-copy\"></i>\n </button>\n <button\n class=\"vs-action-btn\"\n (click)=\"OnConfigureViewById(view.id, $event)\"\n title=\"Configure\">\n <i class=\"fa-solid fa-sliders\"></i>\n </button>\n <button\n class=\"vs-action-btn\"\n (click)=\"OnOpenViewInTab(view.id, $event)\"\n title=\"Open in Tab\">\n <i class=\"fa-solid fa-up-right-from-square\"></i>\n </button>\n </div>\n @if (SelectedViewID === view.id) {\n <i class=\"fa-solid fa-check vs-card-check\"></i>\n }\n </div>\n }\n </div>\n }\n\n <!-- Shared Views Section -->\n @if (FilteredSharedViews.length > 0) {\n <div class=\"vs-section\">\n <div class=\"vs-section-header\">\n <i class=\"fa-solid fa-share-nodes\"></i>\n <span>Shared With Me</span>\n <span class=\"vs-section-count\">{{ FilteredSharedViews.length }}</span>\n </div>\n @for (view of FilteredSharedViews; track view.id) {\n <div\n class=\"vs-card\"\n [class.active]=\"SelectedViewID === view.id\"\n (click)=\"SelectView(view)\">\n <div class=\"vs-card-icon shared\">\n <i class=\"fa-solid fa-users\"></i>\n </div>\n <div class=\"vs-card-content\">\n <div class=\"vs-card-name\">\n {{ view.name }}\n @if (!view.userCanEdit) {\n <span class=\"vs-shared-badge\">View Only</span>\n } @else {\n <span class=\"vs-shared-badge editable\">Can Edit</span>\n }\n </div>\n @if (view.entity.Description) {\n <div class=\"vs-card-desc\">{{ view.entity.Description }}</div>\n }\n @if (GetViewFilterCount(view) > 0 || GetViewColumnCount(view) > 0) {\n <div class=\"vs-card-meta\">\n @if (GetViewFilterCount(view) > 0) {\n <span><i class=\"fa-solid fa-filter\"></i> {{ GetViewFilterCount(view) }} filter{{ GetViewFilterCount(view) !== 1 ? 's' : '' }}</span>\n }\n @if (GetViewColumnCount(view) > 0) {\n <span><i class=\"fa-solid fa-table-columns\"></i> {{ GetViewColumnCount(view) }} col{{ GetViewColumnCount(view) !== 1 ? 's' : '' }}</span>\n }\n </div>\n }\n </div>\n <div class=\"vs-card-actions\">\n <button\n class=\"vs-action-btn\"\n (click)=\"OnDuplicateView(view.id, $event)\"\n title=\"Duplicate as My View\">\n <i class=\"fa-regular fa-copy\"></i>\n </button>\n @if (view.userCanEdit) {\n <button\n class=\"vs-action-btn\"\n (click)=\"OnConfigureViewById(view.id, $event)\"\n title=\"Configure\">\n <i class=\"fa-solid fa-sliders\"></i>\n </button>\n }\n </div>\n @if (SelectedViewID === view.id) {\n <i class=\"fa-solid fa-check vs-card-check\"></i>\n }\n </div>\n }\n </div>\n }\n\n <!-- No Views Message -->\n @if (!HasViews && !IsLoading) {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-bookmark\"\n Title=\"No saved views yet\"\n Message=\"Save your current configuration as a view\" />\n }\n\n <!-- No Search Results -->\n @if (HasViews && FilteredMyViews.length === 0 && FilteredSharedViews.length === 0 && SearchText) {\n <mj-empty-state\n Size=\"compact\"\n Variant=\"no-results\"\n [Title]=\"NoViewsMatchMessage\" />\n }\n\n <!-- Loading Indicator -->\n @if (IsLoading) {\n <div class=\"vs-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading views...</span>\n </div>\n }\n </div>\n\n <!-- Panel Footer -->\n <div class=\"vs-panel-footer\">\n <button\n class=\"vs-panel-btn primary\"\n (click)=\"OnQuickSave()\"\n title=\"Save current view configuration\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>New View</span>\n </button>\n <button\n class=\"vs-panel-btn\"\n (click)=\"OnResetToDefault()\"\n title=\"Reset to default view\">\n <i class=\"fa-solid fa-arrow-rotate-left\"></i>\n <span>Reset to Default</span>\n </button>\n </div>\n </div>\n }\n </div>\n\n <!-- Open in Tab Button (only visible when a view is selected) -->\n @if (SelectedViewID) {\n <button\n class=\"vs-icon-btn\"\n (click)=\"OnOpenInTab()\"\n title=\"Open this view in a new tab\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n </button>\n }\n\n <!-- Configure View Button -->\n <button\n class=\"vs-icon-btn\"\n (click)=\"OnConfigureView()\"\n title=\"Configure view settings (columns, filters, sorting)\">\n <i class=\"fa-solid fa-sliders-h\"></i>\n </button>\n\n <!-- Create New Record Button -->\n <button\n class=\"vs-icon-btn\"\n (click)=\"OnCreateNewRecord()\"\n title=\"Create new record\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n\n <!-- Export Button -->\n <button\n class=\"vs-icon-btn vs-export-btn\"\n (click)=\"OnExport()\"\n title=\"Export\">\n <i class=\"fa-solid fa-file-excel\"></i>\n </button>\n</div>\n", styles: ["/* ========================================\n VIEW SELECTOR - All classes prefixed vs-\n to avoid conflicts (ViewEncapsulation.None)\n ======================================== */\n\n/* Container */\n.vs-container {\n display: flex;\n align-items: center;\n gap: 4px;\n position: relative;\n}\n\n/* Backdrop for closing panel */\n.vs-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 999;\n background: transparent;\n}\n\n.vs-wrapper {\n position: relative;\n}\n\n/* Main Button */\n.vs-button {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n transition: all 0.15s ease;\n min-width: 140px;\n max-width: 280px;\n}\n\n.vs-button:hover {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.vs-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.vs-button.open {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.vs-button.modified {\n border-color: var(--mj-status-warning);\n}\n\n.vs-btn-icon {\n color: var(--mj-brand-primary);\n font-size: 12px;\n}\n\n.vs-btn-name {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n text-align: left;\n}\n\n.vs-modified-dot {\n color: var(--mj-status-warning);\n font-weight: bold;\n font-size: 18px;\n line-height: 1;\n}\n\n.vs-arrow {\n color: var(--mj-text-muted);\n font-size: 10px;\n transition: transform 0.2s ease;\n}\n\n.vs-arrow.rotated {\n transform: rotate(180deg);\n}\n\n/* ========================================\n RICH VIEW PANEL\n Styles ported directly from prototype\n ======================================== */\n\n/* Reset inherited styles inside panel */\n.vs-panel,\n.vs-panel * {\n box-sizing: border-box;\n line-height: 1.5;\n text-align: left;\n}\n\n.vs-panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n width: 400px;\n max-width: calc(100vw - 40px);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16);\n z-index: 1000;\n overflow: hidden;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n/* Panel Header */\n.vs-panel-header {\n padding: 16px 20px;\n display: flex;\n align-items: center;\n gap: 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.vs-header-icon {\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n.vs-panel-title {\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.vs-entity-badge {\n font-size: 11px;\n font-weight: 500;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.vs-close-btn {\n margin-left: auto;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 16px;\n padding: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.vs-close-btn:hover {\n color: var(--mj-text-primary);\n}\n\n/* Panel Search */\n.vs-panel-search {\n padding: 12px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.vs-search-input {\n width: 100%;\n padding: 6px 12px 6px 32px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-size: 13px;\n background: var(--mj-bg-surface-card) url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512' fill='%23999'%3E%3Cpath d='M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208z'/%3E%3C/svg%3E\") no-repeat 10px center;\n background-size: 14px;\n box-sizing: border-box;\n font-family: inherit;\n}\n\n.vs-search-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n}\n\n.vs-search-input::placeholder {\n color: var(--mj-text-muted);\n}\n\n/* Panel Body */\n.vs-panel-body {\n max-height: 400px;\n overflow-y: auto;\n}\n\n/* View Section */\n.vs-section {\n padding: 8px 0;\n}\n\n.vs-section-header {\n padding: 6px 20px;\n font-size: 11px;\n font-weight: 700;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.vs-section-header i {\n font-size: 10px;\n}\n\n.vs-section-count {\n background: var(--mj-border-default);\n padding: 0 6px;\n border-radius: 8px;\n font-size: 10px;\n}\n\n/* View Card */\n.vs-card {\n padding: 10px 20px;\n cursor: pointer;\n transition: background 0.1s;\n display: flex;\n align-items: flex-start;\n gap: 12px;\n}\n\n.vs-card:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.vs-card.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n border-left: 3px solid var(--mj-brand-primary);\n padding-left: 17px;\n}\n\n/* View Card Icon */\n.vs-card-icon {\n width: 32px;\n height: 32px;\n border-radius: 6px;\n background: var(--mj-bg-surface-card);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n font-size: 14px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n.vs-card-icon.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.vs-card-icon.shared {\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-muted);\n}\n\n/* View Card Content */\n.vs-card-content {\n flex: 1;\n min-width: 0;\n padding-top: 4px;\n}\n\n.vs-card-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.vs-card.active .vs-card-name {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.vs-default-badge {\n font-size: 9px;\n font-weight: 600;\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n padding: 1px 5px;\n border-radius: 6px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.vs-shared-badge {\n font-size: 9px;\n font-weight: 600;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n padding: 1px 5px;\n border-radius: 6px;\n}\n\n.vs-shared-badge.editable {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.vs-card-desc {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.vs-card-meta {\n display: flex;\n gap: 10px;\n margin-top: 4px;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.vs-card-meta span {\n display: flex;\n align-items: center;\n gap: 3px;\n}\n\n.vs-card-meta i {\n font-size: 10px;\n}\n\n/* View Card Check */\n.vs-card-check {\n color: var(--mj-brand-primary);\n font-size: 12px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n/* View Card Actions (hover reveal) */\n.vs-card-actions {\n display: flex;\n gap: 2px;\n opacity: 0;\n transition: opacity 0.15s;\n flex-shrink: 0;\n margin-top: 2px;\n}\n\n/* :focus-within matters as much as :hover \u2014 these buttons (Duplicate, Configure,\n Open in Tab) were reachable ONLY by pointer. A keyboard user could tab into them\n and see nothing at opacity 0, and anything driving the page without a hover\n (assistive tech, automation) could not reach them at all. */\n.vs-card:hover .vs-card-actions,\n.vs-card:focus-within .vs-card-actions {\n opacity: 1;\n}\n\n.vs-action-btn {\n width: 26px;\n height: 26px;\n border-radius: 4px;\n border: none;\n background: none;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.vs-action-btn:hover {\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-primary);\n}\n\n/* Loading */\n.vs-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 20px;\n color: var(--mj-text-muted);\n font-size: 13px;\n text-align: center;\n}\n\n/* Panel Footer */\n.vs-panel-footer {\n padding: 12px 20px;\n border-top: 1px solid var(--mj-border-default);\n display: flex;\n gap: 8px;\n}\n\n.vs-panel-btn {\n flex: 1;\n padding: 8px 12px;\n border-radius: 4px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n cursor: pointer;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n transition: all 0.15s;\n font-family: inherit;\n text-align: center;\n}\n\n.vs-panel-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.vs-panel-btn.primary {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n.vs-panel-btn.primary:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.vs-panel-btn i {\n font-size: 11px;\n}\n\n/* ========================================\n ICON BUTTONS (outside panel)\n ======================================== */\n\n.vs-icon-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n cursor: pointer;\n color: var(--mj-text-secondary);\n transition: all 0.15s ease;\n}\n\n.vs-icon-btn:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n color: var(--mj-brand-primary);\n}\n\n.vs-icon-btn i {\n font-size: 12px;\n}\n\n.vs-export-btn:hover {\n color: var(--mj-status-success);\n}\n\n/* Responsive */\n@media (max-width: 768px) {\n .vs-button {\n min-width: 100px;\n max-width: 180px;\n padding: 6px 10px;\n }\n\n .vs-panel {\n width: calc(100vw - 32px);\n max-width: 400px;\n }\n}\n"] }]
875
875
  }], () => [{ type: i0.ChangeDetectorRef }], { Entity: [{
876
876
  type: Input
877
877
  }], SelectedViewID: [{
@@ -54,14 +54,22 @@ export declare class CardsViewRendererComponent extends BaseAngularComponent imp
54
54
  * type's `config`), so the host sets it via a guarded `setInput`. Type matches
55
55
  * {@link EntityCardsComponent.cardTemplate}.
56
56
  */
57
- cardTemplate: CardTemplate | null;
57
+ CardTemplate: CardTemplate | null;
58
+ /** @deprecated Use {@link CardTemplate}. */
59
+ set cardTemplate(value: CardTemplate | null);
60
+ /** @deprecated Use {@link CardTemplate}. */
61
+ get cardTemplate(): CardTemplate | null;
58
62
  /**
59
63
  * Optional map of record primary-key strings → hidden field names that matched the active
60
64
  * filter, used by the cards view to flag matches in non-visible fields. Host-level config,
61
65
  * set by the host via a guarded `setInput`. Type matches
62
66
  * {@link EntityCardsComponent.hiddenFieldMatches}.
63
67
  */
64
- hiddenFieldMatches: Map<string, string>;
68
+ HiddenFieldMatches: Map<string, string>;
69
+ /** @deprecated Use {@link HiddenFieldMatches}. */
70
+ set hiddenFieldMatches(value: Map<string, string>);
71
+ /** @deprecated Use {@link HiddenFieldMatches}. */
72
+ get hiddenFieldMatches(): Map<string, string>;
65
73
  /** Emitted when a record is selected (single click) — payload is the raw record object. */
66
74
  recordSelected: EventEmitter<unknown>;
67
75
  /** Emitted when a record should be opened (double-click / open) — payload is the raw record object. */
@@ -75,14 +83,18 @@ export declare class CardsViewRendererComponent extends BaseAngularComponent imp
75
83
  * Relay `<mj-entity-cards>`'s selection event, normalizing to the raw record the host
76
84
  * expects (the host builds the composite key itself).
77
85
  */
86
+ OnRecordSelected(event: RecordSelectedEvent): void;
87
+ /** @deprecated Use {@link OnRecordSelected}. */
78
88
  onRecordSelected(event: RecordSelectedEvent): void;
79
89
  /**
80
90
  * Relay `<mj-entity-cards>`'s open event, normalizing to the raw record the host expects
81
91
  * (the host builds the composite key itself).
82
92
  */
93
+ OnRecordOpened(event: RecordOpenedEvent): void;
94
+ /** @deprecated Use {@link OnRecordOpened}. */
83
95
  onRecordOpened(event: RecordOpenedEvent): void;
84
96
  static ɵfac: i0.ɵɵFactoryDeclaration<CardsViewRendererComponent, never>;
85
- static ɵcmp: i0.ɵɵComponentDeclaration<CardsViewRendererComponent, "mj-cards-view-renderer", never, { "entity": { "alias": "entity"; "required": false; }; "records": { "alias": "records"; "required": false; }; "selectedRecordId": { "alias": "selectedRecordId"; "required": false; }; "filterText": { "alias": "filterText"; "required": false; }; "config": { "alias": "config"; "required": false; }; "cardTemplate": { "alias": "cardTemplate"; "required": false; }; "hiddenFieldMatches": { "alias": "hiddenFieldMatches"; "required": false; }; }, { "recordSelected": "recordSelected"; "recordOpened": "recordOpened"; "configChanged": "configChanged"; }, never, never, false, never>;
97
+ static ɵcmp: i0.ɵɵComponentDeclaration<CardsViewRendererComponent, "mj-cards-view-renderer", never, { "entity": { "alias": "entity"; "required": false; }; "records": { "alias": "records"; "required": false; }; "selectedRecordId": { "alias": "selectedRecordId"; "required": false; }; "filterText": { "alias": "filterText"; "required": false; }; "config": { "alias": "config"; "required": false; }; "CardTemplate": { "alias": "CardTemplate"; "required": false; }; "cardTemplate": { "alias": "cardTemplate"; "required": false; }; "HiddenFieldMatches": { "alias": "HiddenFieldMatches"; "required": false; }; "hiddenFieldMatches": { "alias": "hiddenFieldMatches"; "required": false; }; }, { "recordSelected": "recordSelected"; "recordOpened": "recordOpened"; "configChanged": "configChanged"; }, never, never, false, never>;
86
98
  }
87
99
  /**
88
100
  * Tree-shaking guard. Force-references this renderer so bundlers (ESBuild/Vite) don't drop the
@@ -1 +1 @@
1
- {"version":3,"file":"cards-view-renderer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/view-types/renderers/cards-view-renderer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAqB,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;;AAGnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBA2Ba,0BAA2B,SAAQ,oBAAqB,YAAW,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAGpH,mDAAmD;IAC1C,MAAM,EAAE,UAAU,GAAG,IAAI,CAAQ;IAE1C,mEAAmE;IAC1D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAM;IAEjD,mEAAmE;IAC1D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEhD,0EAA0E;IACjE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE1C;;;OAGG;IACM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAM;IAI9C;;;;OAIG;IACM,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IAElD;;;;;OAKG;IACM,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAa;IAI7D,2FAA2F;IACjF,cAAc,wBAA+B;IAEvD,uGAAuG;IAC7F,YAAY,wBAA+B;IAErD;;;OAGG;IACO,aAAa,wCAA+C;IAEtE;;;OAGG;IACH,gBAAgB,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI;IAIlD;;;OAGG;IACH,cAAc,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;yCAhEnC,0BAA0B;2CAA1B,0BAA0B;CAmEtC;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C"}
1
+ {"version":3,"file":"cards-view-renderer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/view-types/renderers/cards-view-renderer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAqB,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;;AAGnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBA2Ba,0BAA2B,SAAQ,oBAAqB,YAAW,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAGpH,mDAAmD;IAC1C,MAAM,EAAE,UAAU,GAAG,IAAI,CAAQ;IAE1C,mEAAmE;IAC1D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAM;IAEjD,mEAAmE;IAC1D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEhD,0EAA0E;IACjE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE1C;;;OAGG;IACM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAM;IAI9C;;;;OAIG;IACM,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IAElD,4CAA4C;IAC5C,IAAa,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,EAEnD;IACD,4CAA4C;IAC5C,IAAI,YAAY,IAAI,YAAY,GAAG,IAAI,CAEtC;IAED;;;;;OAKG;IACM,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAa;IAE7D,kDAAkD;IAClD,IAAa,kBAAkB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAEzD;IACD,kDAAkD;IAClD,IAAI,kBAAkB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAE5C;IAID,2FAA2F;IACjF,cAAc,wBAA+B;IAEvD,uGAAuG;IAC7F,YAAY,wBAA+B;IAErD;;;OAGG;IACO,aAAa,wCAA+C;IAEtE;;;OAGG;IACH,gBAAgB,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI;IAIlD,gDAAgD;IAChD,gBAAgB,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI;IAIlD;;;OAGG;IACH,cAAc,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAI9C,8CAA8C;IAC9C,cAAc,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;yCA5FnC,0BAA0B;2CAA1B,0BAA0B;CA+FtC;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C"}
@@ -54,14 +54,30 @@ export class CardsViewRendererComponent extends BaseAngularComponent {
54
54
  * type's `config`), so the host sets it via a guarded `setInput`. Type matches
55
55
  * {@link EntityCardsComponent.cardTemplate}.
56
56
  */
57
- cardTemplate = null;
57
+ CardTemplate = null;
58
+ /** @deprecated Use {@link CardTemplate}. */
59
+ set cardTemplate(value) {
60
+ this.CardTemplate = value;
61
+ }
62
+ /** @deprecated Use {@link CardTemplate}. */
63
+ get cardTemplate() {
64
+ return this.CardTemplate;
65
+ }
58
66
  /**
59
67
  * Optional map of record primary-key strings → hidden field names that matched the active
60
68
  * filter, used by the cards view to flag matches in non-visible fields. Host-level config,
61
69
  * set by the host via a guarded `setInput`. Type matches
62
70
  * {@link EntityCardsComponent.hiddenFieldMatches}.
63
71
  */
64
- hiddenFieldMatches = new Map();
72
+ HiddenFieldMatches = new Map();
73
+ /** @deprecated Use {@link HiddenFieldMatches}. */
74
+ set hiddenFieldMatches(value) {
75
+ this.HiddenFieldMatches = value;
76
+ }
77
+ /** @deprecated Use {@link HiddenFieldMatches}. */
78
+ get hiddenFieldMatches() {
79
+ return this.HiddenFieldMatches;
80
+ }
65
81
  // ---- IViewRenderer outputs ----
66
82
  /** Emitted when a record is selected (single click) — payload is the raw record object. */
67
83
  recordSelected = new EventEmitter();
@@ -76,18 +92,26 @@ export class CardsViewRendererComponent extends BaseAngularComponent {
76
92
  * Relay `<mj-entity-cards>`'s selection event, normalizing to the raw record the host
77
93
  * expects (the host builds the composite key itself).
78
94
  */
79
- onRecordSelected(event) {
95
+ OnRecordSelected(event) {
80
96
  this.recordSelected.emit(event.record);
81
97
  }
98
+ /** @deprecated Use {@link OnRecordSelected}. */
99
+ onRecordSelected(event) {
100
+ return this.OnRecordSelected(event);
101
+ }
82
102
  /**
83
103
  * Relay `<mj-entity-cards>`'s open event, normalizing to the raw record the host expects
84
104
  * (the host builds the composite key itself).
85
105
  */
86
- onRecordOpened(event) {
106
+ OnRecordOpened(event) {
87
107
  this.recordOpened.emit(event.record);
88
108
  }
109
+ /** @deprecated Use {@link OnRecordOpened}. */
110
+ onRecordOpened(event) {
111
+ return this.OnRecordOpened(event);
112
+ }
89
113
  static ɵfac = /*@__PURE__*/ (() => { let ɵCardsViewRendererComponent_BaseFactory; return function CardsViewRendererComponent_Factory(__ngFactoryType__) { return (ɵCardsViewRendererComponent_BaseFactory || (ɵCardsViewRendererComponent_BaseFactory = i0.ɵɵgetInheritedFactory(CardsViewRendererComponent)))(__ngFactoryType__ || CardsViewRendererComponent); }; })();
90
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: CardsViewRendererComponent, selectors: [["mj-cards-view-renderer"]], inputs: { entity: "entity", records: "records", selectedRecordId: "selectedRecordId", filterText: "filterText", config: "config", cardTemplate: "cardTemplate", hiddenFieldMatches: "hiddenFieldMatches" }, outputs: { recordSelected: "recordSelected", recordOpened: "recordOpened", configChanged: "configChanged" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 1, vars: 7, consts: [[3, "recordSelected", "recordOpened", "Provider", "entity", "records", "selectedRecordId", "cardTemplate", "hiddenFieldMatches", "filterText"]], template: function CardsViewRendererComponent_Template(rf, ctx) { if (rf & 1) {
114
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: CardsViewRendererComponent, selectors: [["mj-cards-view-renderer"]], inputs: { entity: "entity", records: "records", selectedRecordId: "selectedRecordId", filterText: "filterText", config: "config", CardTemplate: "CardTemplate", cardTemplate: "cardTemplate", HiddenFieldMatches: "HiddenFieldMatches", hiddenFieldMatches: "hiddenFieldMatches" }, outputs: { recordSelected: "recordSelected", recordOpened: "recordOpened", configChanged: "configChanged" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 1, vars: 7, consts: [[3, "recordSelected", "recordOpened", "Provider", "entity", "records", "selectedRecordId", "cardTemplate", "hiddenFieldMatches", "filterText"]], template: function CardsViewRendererComponent_Template(rf, ctx) { if (rf & 1) {
91
115
  i0.ɵɵelementStart(0, "mj-entity-cards", 0);
92
116
  i0.ɵɵlistener("recordSelected", function CardsViewRendererComponent_Template_mj_entity_cards_recordSelected_0_listener($event) { return ctx.onRecordSelected($event); })("recordOpened", function CardsViewRendererComponent_Template_mj_entity_cards_recordOpened_0_listener($event) { return ctx.onRecordOpened($event); });
93
117
  i0.ɵɵelementEnd();
@@ -121,8 +145,12 @@ export class CardsViewRendererComponent extends BaseAngularComponent {
121
145
  type: Input
122
146
  }], config: [{
123
147
  type: Input
148
+ }], CardTemplate: [{
149
+ type: Input
124
150
  }], cardTemplate: [{
125
151
  type: Input
152
+ }], HiddenFieldMatches: [{
153
+ type: Input
126
154
  }], hiddenFieldMatches: [{
127
155
  type: Input
128
156
  }], recordSelected: [{
@@ -1 +1 @@
1
- {"version":3,"file":"cards-view-renderer.component.js","sourceRoot":"","sources":["../../../../src/lib/view-types/renderers/cards-view-renderer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE1F,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;;AAKrE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AA4BH,MAAM,OAAO,0BAA2B,SAAQ,oBAAoB;IAClE,mEAAmE;IAEnE,mDAAmD;IAC1C,MAAM,GAAsB,IAAI,CAAC;IAE1C,mEAAmE;IAC1D,OAAO,GAA8B,EAAE,CAAC;IAEjD,mEAAmE;IAC1D,gBAAgB,GAAkB,IAAI,CAAC;IAEhD,0EAA0E;IACjE,UAAU,GAAkB,IAAI,CAAC;IAE1C;;;OAGG;IACM,MAAM,GAA4B,EAAE,CAAC;IAE9C,0EAA0E;IAE1E;;;;OAIG;IACM,YAAY,GAAwB,IAAI,CAAC;IAElD;;;;;OAKG;IACM,kBAAkB,GAAwB,IAAI,GAAG,EAAE,CAAC;IAE7D,kCAAkC;IAElC,2FAA2F;IACjF,cAAc,GAAG,IAAI,YAAY,EAAW,CAAC;IAEvD,uGAAuG;IAC7F,YAAY,GAAG,IAAI,YAAY,EAAW,CAAC;IAErD;;;OAGG;IACO,aAAa,GAAG,IAAI,YAAY,EAA2B,CAAC;IAEtE;;;OAGG;IACH,gBAAgB,CAAC,KAA0B;QACzC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC;IAED;;;OAGG;IACH,cAAc,CAAC,KAAwB;QACrC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;qRAlEU,0BAA0B,yBAA1B,0BAA0B;6DAA1B,0BAA0B;YAtBnC,0CAUC;YADC,AADA,wIAAkB,4BAAwB,IAAC,uHAC3B,0BAAsB,IAAC;YAEzC,iBAAkB;;YAJhB,AADA,AADA,AADA,AADA,AADA,AADA,uCAAqB,sBACJ,wBACE,0CACkB,kCACR,8CACY,oCACV;;;iFAexB,0BAA0B;cA3BtC,SAAS;6BACI,KAAK,YACP,wBAAwB,iBACnB,iBAAiB,CAAC,IAAI,YAC3B;;;;;;;;;;;;;GAaT;;kBAcA,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAML,KAAK;;kBASL,KAAK;;kBAQL,KAAK;;kBAKL,MAAM;;kBAGN,MAAM;;kBAMN,MAAM;;kFAlDI,0BAA0B;AAqEvC;;;;GAIG;AACH,MAAM,UAAU,qBAAqB;IACnC,0DAA0D;AAC5D,CAAC","sourcesContent":["import { Component, Input, Output, EventEmitter, ViewEncapsulation } from '@angular/core';\nimport { EntityInfo } from '@memberjunction/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { IViewRenderer } from '../view-type.contracts';\nimport { CardTemplate, RecordSelectedEvent, RecordOpenedEvent } from '../../types';\nimport { EntityCardsComponent } from '../../entity-cards/entity-cards.component';\n\n/**\n * CardsViewRendererComponent\n * --------------------------\n * The Cards **view type** renderer — a thin {@link IViewRenderer} adapter that hosts the\n * existing {@link EntityCardsComponent} (`<mj-entity-cards>`) inside the entity-viewer's\n * pluggable view-type system. It is the dynamic-mounted plug-in that the `CardsViewType`\n * descriptor points at, replacing the descriptor's previous direct reference to\n * `EntityCardsComponent` (which did not honor the {@link IViewRenderer} contract).\n *\n * The host feeds the standard renderer inputs (`entity` / `records` / `selectedRecordId` /\n * `filterText` / `config`) via `setInput` and listens for `recordSelected` / `recordOpened`.\n * Two extra inputs (`cardTemplate`, `hiddenFieldMatches`) reproduce the host-level bindings\n * that `<mj-entity-cards>` was previously given directly in `entity-viewer.component.html` —\n * the host sets them via a guarded `setInput`.\n *\n * **Payload normalization:** `<mj-entity-cards>` emits the rich {@link RecordSelectedEvent} /\n * {@link RecordOpenedEvent} objects (`{ record, entity, compositeKey }`), but the host's\n * dynamic renderer handler expects the **raw record** and builds the composite key itself.\n * This adapter therefore extracts `.record` and re-emits just that object, matching the\n * other plug-in renderers (e.g. the Cluster renderer emits raw record objects).\n *\n * This is an NgModule-declared (`standalone: false`) component, declared in `EntityViewerModule`\n * alongside {@link EntityCardsComponent} — so it renders `<mj-entity-cards>` directly from the\n * module's compilation scope with no `imports` array and no self-import of the module (the module\n * loads the view-type descriptors, which reference these wrappers, so a wrapper importing the\n * module back would form a runtime import cycle — NG0919).\n *\n * Inputs use the camelCase names mandated by the {@link IViewRenderer} contract (the host\n * binds them by those exact names via `setInput`), rather than MJ's usual PascalCase for public\n * members — mirroring the Cluster renderer.\n */\n@Component({\n standalone: false,\n selector: 'mj-cards-view-renderer',\n encapsulation: ViewEncapsulation.None,\n template: `\n <mj-entity-cards\n [Provider]=\"Provider\"\n [entity]=\"entity\"\n [records]=\"records\"\n [selectedRecordId]=\"selectedRecordId\"\n [cardTemplate]=\"cardTemplate\"\n [hiddenFieldMatches]=\"hiddenFieldMatches\"\n [filterText]=\"filterText ?? ''\"\n (recordSelected)=\"onRecordSelected($event)\"\n (recordOpened)=\"onRecordOpened($event)\"\n >\n </mj-entity-cards>\n `,\n styles: [\n `\n :host {\n display: block;\n height: 100%;\n }\n `,\n ],\n})\nexport class CardsViewRendererComponent extends BaseAngularComponent implements IViewRenderer<Record<string, unknown>> {\n // ---- IViewRenderer inputs (camelCase per the host contract) ----\n\n /** The entity whose records are being rendered. */\n @Input() entity: EntityInfo | null = null;\n\n /** The records to render (already loaded/filtered by the host). */\n @Input() records: Record<string, unknown>[] = [];\n\n /** Primary-key string of the currently selected record, if any. */\n @Input() selectedRecordId: string | null = null;\n\n /** Active filter text (used by the cards view for match highlighting). */\n @Input() filterText: string | null = null;\n\n /**\n * View-type-specific configuration. Cards has no per-view config — this is accepted to\n * satisfy the {@link IViewRenderer} contract and is otherwise unused (no-op).\n */\n @Input() config: Record<string, unknown> = {};\n\n // ---- Host-level inputs for <mj-entity-cards> (not per-view config) ----\n\n /**\n * Optional custom card template. This is host-level configuration (not stored in the view\n * type's `config`), so the host sets it via a guarded `setInput`. Type matches\n * {@link EntityCardsComponent.cardTemplate}.\n */\n @Input() cardTemplate: CardTemplate | null = null;\n\n /**\n * Optional map of record primary-key strings → hidden field names that matched the active\n * filter, used by the cards view to flag matches in non-visible fields. Host-level config,\n * set by the host via a guarded `setInput`. Type matches\n * {@link EntityCardsComponent.hiddenFieldMatches}.\n */\n @Input() hiddenFieldMatches: Map<string, string> = new Map();\n\n // ---- IViewRenderer outputs ----\n\n /** Emitted when a record is selected (single click) — payload is the raw record object. */\n @Output() recordSelected = new EventEmitter<unknown>();\n\n /** Emitted when a record should be opened (double-click / open) — payload is the raw record object. */\n @Output() recordOpened = new EventEmitter<unknown>();\n\n /**\n * Required by {@link IViewRenderer}; the cards view never mutates its own config, so this\n * never emits. Declared so the host's uniform output subscription is satisfied.\n */\n @Output() configChanged = new EventEmitter<Record<string, unknown>>();\n\n /**\n * Relay `<mj-entity-cards>`'s selection event, normalizing to the raw record the host\n * expects (the host builds the composite key itself).\n */\n onRecordSelected(event: RecordSelectedEvent): void {\n this.recordSelected.emit(event.record);\n }\n\n /**\n * Relay `<mj-entity-cards>`'s open event, normalizing to the raw record the host expects\n * (the host builds the composite key itself).\n */\n onRecordOpened(event: RecordOpenedEvent): void {\n this.recordOpened.emit(event.record);\n }\n}\n\n/**\n * Tree-shaking guard. Force-references this renderer so bundlers (ESBuild/Vite) don't drop the\n * component in builds that only mount it dynamically via the ClassFactory/descriptor. Mirrors\n * the Cluster renderer's load guard; the parent wires this into a barrel/module load path.\n */\nexport function LoadCardsViewRenderer(): void {\n // no-op; presence prevents tree-shaking of this component\n}\n"]}
1
+ {"version":3,"file":"cards-view-renderer.component.js","sourceRoot":"","sources":["../../../../src/lib/view-types/renderers/cards-view-renderer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE1F,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;;AAKrE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AA4BH,MAAM,OAAO,0BAA2B,SAAQ,oBAAoB;IAClE,mEAAmE;IAEnE,mDAAmD;IAC1C,MAAM,GAAsB,IAAI,CAAC;IAE1C,mEAAmE;IAC1D,OAAO,GAA8B,EAAE,CAAC;IAEjD,mEAAmE;IAC1D,gBAAgB,GAAkB,IAAI,CAAC;IAEhD,0EAA0E;IACjE,UAAU,GAAkB,IAAI,CAAC;IAE1C;;;OAGG;IACM,MAAM,GAA4B,EAAE,CAAC;IAE9C,0EAA0E;IAE1E;;;;OAIG;IACM,YAAY,GAAwB,IAAI,CAAC;IAElD,4CAA4C;IAC5C,IAAa,YAAY,CAAC,KAA0B;QAClD,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;IAC5B,CAAC;IACD,4CAA4C;IAC5C,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED;;;;;OAKG;IACM,kBAAkB,GAAwB,IAAI,GAAG,EAAE,CAAC;IAE7D,kDAAkD;IAClD,IAAa,kBAAkB,CAAC,KAA0B;QACxD,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;IAClC,CAAC;IACD,kDAAkD;IAClD,IAAI,kBAAkB;QACpB,OAAO,IAAI,CAAC,kBAAkB,CAAC;IACjC,CAAC;IAED,kCAAkC;IAElC,2FAA2F;IACjF,cAAc,GAAG,IAAI,YAAY,EAAW,CAAC;IAEvD,uGAAuG;IAC7F,YAAY,GAAG,IAAI,YAAY,EAAW,CAAC;IAErD;;;OAGG;IACO,aAAa,GAAG,IAAI,YAAY,EAA2B,CAAC;IAEtE;;;OAGG;IACH,gBAAgB,CAAC,KAA0B;QACzC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC;IAED,gDAAgD;IAChD,gBAAgB,CAAC,KAA0B;QACzC,OAAO,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IAED;;;OAGG;IACH,cAAc,CAAC,KAAwB;QACrC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,8CAA8C;IAC9C,cAAc,CAAC,KAAwB;QACrC,OAAO,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;qRA9FU,0BAA0B,yBAA1B,0BAA0B;6DAA1B,0BAA0B;YAtBnC,0CAUC;YADC,AADA,wIAAkB,4BAAwB,IAAC,uHAC3B,0BAAsB,IAAC;YAEzC,iBAAkB;;YAJhB,AADA,AADA,AADA,AADA,AADA,AADA,uCAAqB,sBACJ,wBACE,0CACkB,kCACR,8CACY,oCACV;;;iFAexB,0BAA0B;cA3BtC,SAAS;6BACI,KAAK,YACP,wBAAwB,iBACnB,iBAAiB,CAAC,IAAI,YAC3B;;;;;;;;;;;;;GAaT;;kBAcA,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAML,KAAK;;kBASL,KAAK;;kBAGL,KAAK;;kBAcL,KAAK;;kBAGL,KAAK;;kBAWL,MAAM;;kBAGN,MAAM;;kBAMN,MAAM;;kFApEI,0BAA0B;AAiGvC;;;;GAIG;AACH,MAAM,UAAU,qBAAqB;IACnC,0DAA0D;AAC5D,CAAC","sourcesContent":["import { Component, Input, Output, EventEmitter, ViewEncapsulation } from '@angular/core';\nimport { EntityInfo } from '@memberjunction/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { IViewRenderer } from '../view-type.contracts';\nimport { CardTemplate, RecordSelectedEvent, RecordOpenedEvent } from '../../types';\nimport { EntityCardsComponent } from '../../entity-cards/entity-cards.component';\n\n/**\n * CardsViewRendererComponent\n * --------------------------\n * The Cards **view type** renderer — a thin {@link IViewRenderer} adapter that hosts the\n * existing {@link EntityCardsComponent} (`<mj-entity-cards>`) inside the entity-viewer's\n * pluggable view-type system. It is the dynamic-mounted plug-in that the `CardsViewType`\n * descriptor points at, replacing the descriptor's previous direct reference to\n * `EntityCardsComponent` (which did not honor the {@link IViewRenderer} contract).\n *\n * The host feeds the standard renderer inputs (`entity` / `records` / `selectedRecordId` /\n * `filterText` / `config`) via `setInput` and listens for `recordSelected` / `recordOpened`.\n * Two extra inputs (`cardTemplate`, `hiddenFieldMatches`) reproduce the host-level bindings\n * that `<mj-entity-cards>` was previously given directly in `entity-viewer.component.html` —\n * the host sets them via a guarded `setInput`.\n *\n * **Payload normalization:** `<mj-entity-cards>` emits the rich {@link RecordSelectedEvent} /\n * {@link RecordOpenedEvent} objects (`{ record, entity, compositeKey }`), but the host's\n * dynamic renderer handler expects the **raw record** and builds the composite key itself.\n * This adapter therefore extracts `.record` and re-emits just that object, matching the\n * other plug-in renderers (e.g. the Cluster renderer emits raw record objects).\n *\n * This is an NgModule-declared (`standalone: false`) component, declared in `EntityViewerModule`\n * alongside {@link EntityCardsComponent} — so it renders `<mj-entity-cards>` directly from the\n * module's compilation scope with no `imports` array and no self-import of the module (the module\n * loads the view-type descriptors, which reference these wrappers, so a wrapper importing the\n * module back would form a runtime import cycle — NG0919).\n *\n * Inputs use the camelCase names mandated by the {@link IViewRenderer} contract (the host\n * binds them by those exact names via `setInput`), rather than MJ's usual PascalCase for public\n * members — mirroring the Cluster renderer.\n */\n@Component({\n standalone: false,\n selector: 'mj-cards-view-renderer',\n encapsulation: ViewEncapsulation.None,\n template: `\n <mj-entity-cards\n [Provider]=\"Provider\"\n [entity]=\"entity\"\n [records]=\"records\"\n [selectedRecordId]=\"selectedRecordId\"\n [cardTemplate]=\"cardTemplate\"\n [hiddenFieldMatches]=\"hiddenFieldMatches\"\n [filterText]=\"filterText ?? ''\"\n (recordSelected)=\"onRecordSelected($event)\"\n (recordOpened)=\"onRecordOpened($event)\"\n >\n </mj-entity-cards>\n `,\n styles: [\n `\n :host {\n display: block;\n height: 100%;\n }\n `,\n ],\n})\nexport class CardsViewRendererComponent extends BaseAngularComponent implements IViewRenderer<Record<string, unknown>> {\n // ---- IViewRenderer inputs (camelCase per the host contract) ----\n\n /** The entity whose records are being rendered. */\n @Input() entity: EntityInfo | null = null;\n\n /** The records to render (already loaded/filtered by the host). */\n @Input() records: Record<string, unknown>[] = [];\n\n /** Primary-key string of the currently selected record, if any. */\n @Input() selectedRecordId: string | null = null;\n\n /** Active filter text (used by the cards view for match highlighting). */\n @Input() filterText: string | null = null;\n\n /**\n * View-type-specific configuration. Cards has no per-view config — this is accepted to\n * satisfy the {@link IViewRenderer} contract and is otherwise unused (no-op).\n */\n @Input() config: Record<string, unknown> = {};\n\n // ---- Host-level inputs for <mj-entity-cards> (not per-view config) ----\n\n /**\n * Optional custom card template. This is host-level configuration (not stored in the view\n * type's `config`), so the host sets it via a guarded `setInput`. Type matches\n * {@link EntityCardsComponent.cardTemplate}.\n */\n @Input() CardTemplate: CardTemplate | null = null;\n\n /** @deprecated Use {@link CardTemplate}. */\n @Input() set cardTemplate(value: CardTemplate | null) {\n this.CardTemplate = value;\n }\n /** @deprecated Use {@link CardTemplate}. */\n get cardTemplate(): CardTemplate | null {\n return this.CardTemplate;\n }\n\n /**\n * Optional map of record primary-key strings → hidden field names that matched the active\n * filter, used by the cards view to flag matches in non-visible fields. Host-level config,\n * set by the host via a guarded `setInput`. Type matches\n * {@link EntityCardsComponent.hiddenFieldMatches}.\n */\n @Input() HiddenFieldMatches: Map<string, string> = new Map();\n\n /** @deprecated Use {@link HiddenFieldMatches}. */\n @Input() set hiddenFieldMatches(value: Map<string, string>) {\n this.HiddenFieldMatches = value;\n }\n /** @deprecated Use {@link HiddenFieldMatches}. */\n get hiddenFieldMatches(): Map<string, string> {\n return this.HiddenFieldMatches;\n }\n\n // ---- IViewRenderer outputs ----\n\n /** Emitted when a record is selected (single click) — payload is the raw record object. */\n @Output() recordSelected = new EventEmitter<unknown>();\n\n /** Emitted when a record should be opened (double-click / open) — payload is the raw record object. */\n @Output() recordOpened = new EventEmitter<unknown>();\n\n /**\n * Required by {@link IViewRenderer}; the cards view never mutates its own config, so this\n * never emits. Declared so the host's uniform output subscription is satisfied.\n */\n @Output() configChanged = new EventEmitter<Record<string, unknown>>();\n\n /**\n * Relay `<mj-entity-cards>`'s selection event, normalizing to the raw record the host\n * expects (the host builds the composite key itself).\n */\n OnRecordSelected(event: RecordSelectedEvent): void {\n this.recordSelected.emit(event.record);\n }\n\n /** @deprecated Use {@link OnRecordSelected}. */\n onRecordSelected(event: RecordSelectedEvent): void {\n return this.OnRecordSelected(event);\n }\n\n /**\n * Relay `<mj-entity-cards>`'s open event, normalizing to the raw record the host expects\n * (the host builds the composite key itself).\n */\n OnRecordOpened(event: RecordOpenedEvent): void {\n this.recordOpened.emit(event.record);\n }\n\n /** @deprecated Use {@link OnRecordOpened}. */\n onRecordOpened(event: RecordOpenedEvent): void {\n return this.OnRecordOpened(event);\n }\n}\n\n/**\n * Tree-shaking guard. Force-references this renderer so bundlers (ESBuild/Vite) don't drop the\n * component in builds that only mount it dynamically via the ClassFactory/descriptor. Mirrors\n * the Cluster renderer's load guard; the parent wires this into a barrel/module load path.\n */\nexport function LoadCardsViewRenderer(): void {\n // no-op; presence prevents tree-shaking of this component\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { EventEmitter, OnDestroy } from '@angular/core';
2
2
  import { EntityInfo, RunViewParams, CompositeKey } from '@memberjunction/core';
3
+ import { ExportColumn } from '@memberjunction/export-engine';
3
4
  import { type FieldComparison, type MergeConfig, type MergeConfirmedEvent } from '@memberjunction/ng-record-merge';
4
5
  import { BaseAngularComponent } from '@memberjunction/ng-base-types';
5
6
  import { PageChangeEvent } from '@memberjunction/ng-pagination';
@@ -113,7 +114,11 @@ export declare class GridViewRendererComponent extends BaseAngularComponent impl
113
114
  * Optional full-result-set fetcher supplied by the host, forwarded to the grid's ExportDataProvider
114
115
  * so exports cover every matching row rather than just the loaded page (bug C1).
115
116
  */
116
- exportDataProvider: (() => Promise<Record<string, unknown>[]>) | null;
117
+ ExportDataProvider: (() => Promise<Record<string, unknown>[]>) | null;
118
+ /** @deprecated Use {@link ExportDataProvider}. */
119
+ set exportDataProvider(value: (() => Promise<Record<string, unknown>[]>) | null);
120
+ /** @deprecated Use {@link ExportDataProvider}. */
121
+ get exportDataProvider(): (() => Promise<Record<string, unknown>[]>) | null;
117
122
  /** Primary-key string of the currently selected record, if any. */
118
123
  selectedRecordId: string | null;
119
124
  /** Active filter text — passed through for the grid's cell highlighting. */
@@ -171,6 +176,8 @@ export declare class GridViewRendererComponent extends BaseAngularComponent impl
171
176
  * @returns true when the export succeeded, false when the grid isn't ready or the export failed.
172
177
  */
173
178
  exportRecords(format?: 'csv' | 'excel' | 'json'): Promise<boolean>;
179
+ /** The hosted grid's on-screen columns ({@link IViewRenderer.GetExportColumns}); empty before it mounts. */
180
+ GetExportColumns(): ExportColumn[];
174
181
  /** Whether the add-to-list dialog is visible. Toggled internally by {@link onAddToListRequested}. */
175
182
  protected showListManagementDialog: boolean;
176
183
  /** Config fed into `<mj-list-management-dialog>`. Built from the entity + selected records. */
@@ -182,32 +189,50 @@ export declare class GridViewRendererComponent extends BaseAngularComponent impl
182
189
  /** Dynamic message for the delete-confirmation dialog (reflects the staged record count). */
183
190
  protected deleteConfirmMessage: string;
184
191
  /** Effective toolbar visibility — defaults to `true` when not set in config. */
192
+ get EffectiveShowToolbar(): boolean;
193
+ /** @deprecated Use {@link EffectiveShowToolbar}. */
185
194
  get effectiveShowToolbar(): boolean;
186
195
  /**
187
196
  * Grid search defaults OFF when hosted in entity-viewer. The container already owns
188
197
  * `filterText` ("Filter records..."). Opt in with `toolbarConfig.showSearch: true`.
189
198
  */
199
+ get EffectiveShowSearch(): boolean;
200
+ /** @deprecated Use {@link EffectiveShowSearch}. */
190
201
  get effectiveShowSearch(): boolean;
191
202
  /** Effective selection mode — defaults to `'checkbox'` when not set in config. */
203
+ get EffectiveSelectionMode(): GridSelectionMode;
204
+ /** @deprecated Use {@link EffectiveSelectionMode}. */
192
205
  get effectiveSelectionMode(): GridSelectionMode;
193
206
  /** Effective add-to-list button visibility — defaults to `true` when not set in config. */
207
+ get EffectiveShowAddToListButton(): boolean;
208
+ /** @deprecated Use {@link EffectiveShowAddToListButton}. */
194
209
  get effectiveShowAddToListButton(): boolean;
195
210
  /** Effective pager visibility — defaults to `true` when not set in config. */
211
+ get EffectiveShowPager(): boolean;
212
+ /** @deprecated Use {@link EffectiveShowPager}. */
196
213
  get effectiveShowPager(): boolean;
197
214
  /** Effective page size — config wins, else the generic `pageSize` input, else the grid's own default. */
215
+ get EffectivePageSize(): number;
216
+ /** @deprecated Use {@link EffectivePageSize}. */
198
217
  get effectivePageSize(): number;
199
218
  /**
200
219
  * Effective RunViewParams for the grid. Uses `config.params` when supplied; otherwise builds a
201
220
  * minimal dynamic-view params object from {@link entity}. Returns `null` when no entity is known
202
221
  * yet (the grid renders empty until the host provides one).
203
222
  */
223
+ get EffectiveParams(): RunViewParams | null;
224
+ /** @deprecated Use {@link EffectiveParams}. */
204
225
  get effectiveParams(): RunViewParams | null;
205
226
  /**
206
227
  * Row single-click → {@link recordSelected}. Extracts the raw record (`event.row`) so the host
207
228
  * builds the composite key itself, matching the other renderers.
208
229
  */
230
+ OnAfterRowClick(event: AfterRowClickEventArgs): void;
231
+ /** @deprecated Use {@link OnAfterRowClick}. */
209
232
  onAfterRowClick(event: AfterRowClickEventArgs): void;
210
233
  /** Row double-click → {@link recordOpened} (NAVIGATION), emitting the raw record object. */
234
+ OnAfterRowDoubleClick(event: AfterRowDoubleClickEventArgs): void;
235
+ /** @deprecated Use {@link OnAfterRowDoubleClick}. */
211
236
  onAfterRowDoubleClick(event: AfterRowDoubleClickEventArgs): void;
212
237
  /**
213
238
  * Sort change → both generic coordination channels:
@@ -215,17 +240,23 @@ export declare class GridViewRendererComponent extends BaseAngularComponent impl
215
240
  * 2. persists the sort into `config.gridState.sortSettings` and emits {@link configChanged} so
216
241
  * the grid's sort survives view reloads via the opaque config channel.
217
242
  */
243
+ OnAfterSort(event: AfterSortEventArgs): void;
244
+ /** @deprecated Use {@link OnAfterSort}. */
218
245
  onAfterSort(event: AfterSortEventArgs): void;
219
246
  /**
220
247
  * Generic grid-state change (column resize / reorder / visibility) → merge the grid's updated
221
248
  * {@link ViewGridState} into the opaque config and emit {@link configChanged}; the host persists
222
249
  * the blob verbatim. No `dataRequest` here — column changes don't alter the loaded record set.
223
250
  */
251
+ OnGridStateChanged(event: GridStateChangedEvent): void;
252
+ /** @deprecated Use {@link OnGridStateChanged}. */
224
253
  onGridStateChanged(event: GridStateChangedEvent): void;
225
254
  /**
226
255
  * Selection change → kept INTERNAL. The grid drives its own add-to-list from the selection, so the
227
256
  * wrapper has no need to surface it. We intentionally do not bubble selection anywhere.
228
257
  */
258
+ OnSelectionChange(_selectedKeys: string[]): void;
259
+ /** @deprecated Use {@link OnSelectionChange}. */
229
260
  onSelectionChange(_selectedKeys: string[]): void;
230
261
  /**
231
262
  * Foreign-key link click → {@link openRelatedRecordRequested} (NAVIGATION). Maps the grid's
@@ -233,20 +264,28 @@ export declare class GridViewRendererComponent extends BaseAngularComponent impl
233
264
  * {@link ViewRelatedRecordNavigation} the container forwards to the outer app for routing. The
234
265
  * related entity name is taken from the event when present, else resolved from the metadata by ID.
235
266
  */
267
+ OnForeignKeyClick(event: ForeignKeyClickEvent): void;
268
+ /** @deprecated Use {@link OnForeignKeyClick}. */
236
269
  onForeignKeyClick(event: ForeignKeyClickEvent): void;
237
270
  /** New button → {@link createRecordRequested} (NAVIGATION) — opening the create form is routing. */
271
+ OnNewButtonClick(): void;
272
+ /** @deprecated Use {@link OnNewButtonClick}. */
238
273
  onNewButtonClick(): void;
239
274
  /**
240
275
  * Pager navigation → generic {@link dataRequest}. Maps the grid's {@link PageChangeEvent}
241
276
  * (`PageNumber` is already 1-based, `PageSize`) onto the generic `{ page, pageSize }` shape the
242
277
  * host honors against its own RunView.
243
278
  */
279
+ OnPageChange(event: PageChangeEvent): void;
280
+ /** @deprecated Use {@link OnPageChange}. */
244
281
  onPageChange(event: PageChangeEvent): void;
245
282
  /**
246
283
  * Refresh button → re-emit {@link dataRequest} so the host reloads the current page. This is
247
284
  * generic container ↔ plug-in coordination (a data-access request), NOT a feature event surfaced
248
285
  * to the outer app.
249
286
  */
287
+ OnRefreshButtonClick(): void;
288
+ /** @deprecated Use {@link OnRefreshButtonClick}. */
250
289
  onRefreshButtonClick(): void;
251
290
  /**
252
291
  * Add-to-list button → build a {@link ListManagementDialogConfig} from the entity + the records the
@@ -254,14 +293,24 @@ export declare class GridViewRendererComponent extends BaseAngularComponent impl
254
293
  * changes itself — nothing bubbles up. Mirrors the config construction the legacy host did in
255
294
  * `data-explorer-dashboard`'s `onAddToListRequested()`.
256
295
  */
296
+ OnAddToListRequested(event: {
297
+ entityInfo: EntityInfo;
298
+ records: Record<string, unknown>[];
299
+ recordIds: string[];
300
+ }): void;
301
+ /** @deprecated Use {@link OnAddToListRequested}. */
257
302
  onAddToListRequested(event: {
258
303
  entityInfo: EntityInfo;
259
304
  records: Record<string, unknown>[];
260
305
  recordIds: string[];
261
306
  }): void;
262
307
  /** List-management dialog completed (membership changes applied) → tear down state. */
308
+ OnListManagementComplete(_result: ListManagementResult): void;
309
+ /** @deprecated Use {@link OnListManagementComplete}. */
263
310
  onListManagementComplete(_result: ListManagementResult): void;
264
311
  /** List-management dialog cancelled → tear down state. */
312
+ OnListManagementCancel(): void;
313
+ /** @deprecated Use {@link OnListManagementCancel}. */
265
314
  onListManagementCancel(): void;
266
315
  /**
267
316
  * Resolve a human-friendly display name for a record using the entity's NameField, falling back to
@@ -279,6 +328,8 @@ export declare class GridViewRendererComponent extends BaseAngularComponent impl
279
328
  * Delete button → stage the records and open the Generic confirm dialog. The actual delete happens
280
329
  * in {@link onDeleteConfirmed} after the user confirms — nothing bubbles up.
281
330
  */
331
+ OnDeleteButtonClick(records: Record<string, unknown>[]): void;
332
+ /** @deprecated Use {@link OnDeleteButtonClick}. */
282
333
  onDeleteButtonClick(records: Record<string, unknown>[]): void;
283
334
  /**
284
335
  * Delete confirmed → delete each staged record through the MJ entity layer
@@ -286,8 +337,12 @@ export declare class GridViewRendererComponent extends BaseAngularComponent impl
286
337
  * result and surfacing failures via {@link LogError}. After deletion, re-request the current page
287
338
  * so the host reloads. Self-contained — no feature event leaves the wrapper.
288
339
  */
340
+ OnDeleteConfirmed(): Promise<void>;
341
+ /** @deprecated Use {@link OnDeleteConfirmed}. */
289
342
  onDeleteConfirmed(): Promise<void>;
290
343
  /** Delete cancelled → discard the staged records and close the confirm dialog. */
344
+ OnDeleteCancelled(): void;
345
+ /** @deprecated Use {@link OnDeleteCancelled}. */
291
346
  onDeleteCancelled(): void;
292
347
  /**
293
348
  * The in-flight merge, or null when the panel is closed. Holds everything the panel needs
@@ -315,6 +370,8 @@ export declare class GridViewRendererComponent extends BaseAngularComponent impl
315
370
  private readonly comparison;
316
371
  ngOnDestroy(): void;
317
372
  /** Dismiss the merge notice from its own close button. */
373
+ OnDismissMergeNotice(): void;
374
+ /** @deprecated Use {@link OnDismissMergeNotice}. */
318
375
  onDismissMergeNotice(): void;
319
376
  /**
320
377
  * Set (or clear) the merge notice, replacing any pending auto-dismiss. Every write to
@@ -332,16 +389,27 @@ export declare class GridViewRendererComponent extends BaseAngularComponent impl
332
389
  * The grid asked to merge the selected rows. Compare them, preview what will move, and open
333
390
  * the panel — nothing is written until the user confirms.
334
391
  */
392
+ OnMergeRequested(event: {
393
+ entityInfo: EntityInfo;
394
+ records: Record<string, unknown>[];
395
+ }): Promise<void>;
396
+ /** @deprecated Use {@link OnMergeRequested}. */
335
397
  onMergeRequested(event: {
336
398
  entityInfo: EntityInfo;
337
399
  records: Record<string, unknown>[];
338
400
  }): Promise<void>;
339
401
  /** The user chose which record survives; differing fields default to that record's values. */
402
+ OnSurvivorChange(side: 'left' | 'right'): void;
403
+ /** @deprecated Use {@link OnSurvivorChange}. */
340
404
  onSurvivorChange(side: 'left' | 'right'): void;
341
405
  private prepareMerge;
342
406
  /** The user confirmed: merge, then reload the page so the loser disappears from the grid. */
407
+ OnMergeConfirmed(event: MergeConfirmedEvent): Promise<void>;
408
+ /** @deprecated Use {@link OnMergeConfirmed}. */
343
409
  onMergeConfirmed(event: MergeConfirmedEvent): Promise<void>;
344
410
  /** Merge cancelled → close the panel, write nothing. */
411
+ OnMergeCancelled(): void;
412
+ /** @deprecated Use {@link OnMergeCancelled}. */
345
413
  onMergeCancelled(): void;
346
414
  /**
347
415
  * Fields whose resolution has to travel, and only those: the survivor already holds the value
@@ -386,7 +454,7 @@ export declare class GridViewRendererComponent extends BaseAngularComponent impl
386
454
  */
387
455
  private resolveEntityNameById;
388
456
  static ɵfac: i0.ɵɵFactoryDeclaration<GridViewRendererComponent, never>;
389
- static ɵcmp: i0.ɵɵComponentDeclaration<GridViewRendererComponent, "mj-grid-view-renderer", never, { "entity": { "alias": "entity"; "required": false; }; "records": { "alias": "records"; "required": false; }; "exportDataProvider": { "alias": "exportDataProvider"; "required": false; }; "selectedRecordId": { "alias": "selectedRecordId"; "required": false; }; "filterText": { "alias": "filterText"; "required": false; }; "config": { "alias": "config"; "required": false; }; "AutoLoadEntityActions": { "alias": "AutoLoadEntityActions"; "required": false; }; "totalRecordCount": { "alias": "totalRecordCount"; "required": false; }; "page": { "alias": "page"; "required": false; }; "pageSize": { "alias": "pageSize"; "required": false; }; "isLoading": { "alias": "isLoading"; "required": false; }; }, { "recordSelected": "recordSelected"; "recordOpened": "recordOpened"; "configChanged": "configChanged"; "dataRequest": "dataRequest"; "configureRequested": "configureRequested"; "openRelatedRecordRequested": "openRelatedRecordRequested"; "createRecordRequested": "createRecordRequested"; }, never, never, false, never>;
457
+ static ɵcmp: i0.ɵɵComponentDeclaration<GridViewRendererComponent, "mj-grid-view-renderer", never, { "entity": { "alias": "entity"; "required": false; }; "records": { "alias": "records"; "required": false; }; "ExportDataProvider": { "alias": "ExportDataProvider"; "required": false; }; "exportDataProvider": { "alias": "exportDataProvider"; "required": false; }; "selectedRecordId": { "alias": "selectedRecordId"; "required": false; }; "filterText": { "alias": "filterText"; "required": false; }; "config": { "alias": "config"; "required": false; }; "AutoLoadEntityActions": { "alias": "AutoLoadEntityActions"; "required": false; }; "totalRecordCount": { "alias": "totalRecordCount"; "required": false; }; "page": { "alias": "page"; "required": false; }; "pageSize": { "alias": "pageSize"; "required": false; }; "isLoading": { "alias": "isLoading"; "required": false; }; }, { "recordSelected": "recordSelected"; "recordOpened": "recordOpened"; "configChanged": "configChanged"; "dataRequest": "dataRequest"; "configureRequested": "configureRequested"; "openRelatedRecordRequested": "openRelatedRecordRequested"; "createRecordRequested": "createRecordRequested"; }, never, never, false, never>;
390
458
  }
391
459
  /**
392
460
  * Tree-shaking guard. Force-references this renderer so bundlers (ESBuild/Vite) don't drop the