@memberjunction/ng-entity-viewer 6.1.4 → 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.
- package/dist/lib/aggregate-panel/aggregate-panel.component.d.ts +18 -2
- package/dist/lib/aggregate-panel/aggregate-panel.component.d.ts.map +1 -1
- package/dist/lib/aggregate-panel/aggregate-panel.component.js +47 -18
- package/dist/lib/aggregate-panel/aggregate-panel.component.js.map +1 -1
- package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.d.ts +22 -0
- package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.d.ts.map +1 -1
- package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.js +57 -13
- package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.js.map +1 -1
- package/dist/lib/entity-cards/entity-cards.component.d.ts +68 -5
- package/dist/lib/entity-cards/entity-cards.component.d.ts.map +1 -1
- package/dist/lib/entity-cards/entity-cards.component.js +213 -73
- package/dist/lib/entity-cards/entity-cards.component.js.map +1 -1
- package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts +156 -14
- package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts.map +1 -1
- package/dist/lib/entity-data-grid/entity-data-grid.component.js +432 -138
- package/dist/lib/entity-data-grid/entity-data-grid.component.js.map +1 -1
- package/dist/lib/entity-data-grid/events/grid-events.js +67 -67
- package/dist/lib/entity-data-grid/events/grid-events.js.map +1 -1
- package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.d.ts +112 -8
- package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.d.ts.map +1 -1
- package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.js +255 -69
- package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.js.map +1 -1
- package/dist/lib/entity-viewer/entity-viewer.component.d.ts +11 -0
- package/dist/lib/entity-viewer/entity-viewer.component.d.ts.map +1 -1
- package/dist/lib/entity-viewer/entity-viewer.component.js +45 -8
- package/dist/lib/entity-viewer/entity-viewer.component.js.map +1 -1
- package/dist/lib/pill/pill.component.d.ts +13 -3
- package/dist/lib/pill/pill.component.d.ts.map +1 -1
- package/dist/lib/pill/pill.component.js +34 -10
- package/dist/lib/pill/pill.component.js.map +1 -1
- package/dist/lib/quick-save-dialog/quick-save-dialog.component.d.ts +29 -3
- package/dist/lib/quick-save-dialog/quick-save-dialog.component.d.ts.map +1 -1
- package/dist/lib/quick-save-dialog/quick-save-dialog.component.js +173 -114
- package/dist/lib/quick-save-dialog/quick-save-dialog.component.js.map +1 -1
- package/dist/lib/recycle-bin/recycle-bin.component.d.ts +4 -0
- package/dist/lib/recycle-bin/recycle-bin.component.d.ts.map +1 -1
- package/dist/lib/recycle-bin/recycle-bin.component.js +10 -2
- package/dist/lib/recycle-bin/recycle-bin.component.js.map +1 -1
- package/dist/lib/utils/aggregate-field.util.d.ts +21 -0
- package/dist/lib/utils/aggregate-field.util.d.ts.map +1 -0
- package/dist/lib/utils/aggregate-field.util.js +40 -0
- package/dist/lib/utils/aggregate-field.util.js.map +1 -0
- package/dist/lib/utils/highlight.util.d.ts +6 -0
- package/dist/lib/utils/highlight.util.d.ts.map +1 -1
- package/dist/lib/utils/highlight.util.js +23 -11
- package/dist/lib/utils/highlight.util.js.map +1 -1
- package/dist/lib/utils/record.util.d.ts +10 -0
- package/dist/lib/utils/record.util.d.ts.map +1 -1
- package/dist/lib/utils/record.util.js +20 -4
- package/dist/lib/utils/record.util.js.map +1 -1
- package/dist/lib/view-config-panel/view-config-panel.component.d.ts +22 -2
- package/dist/lib/view-config-panel/view-config-panel.component.d.ts.map +1 -1
- package/dist/lib/view-config-panel/view-config-panel.component.js +54 -14
- package/dist/lib/view-config-panel/view-config-panel.component.js.map +1 -1
- package/dist/lib/view-selector/view-selector.component.js +2 -2
- package/dist/lib/view-types/renderers/cards-view-renderer.component.d.ts +15 -3
- package/dist/lib/view-types/renderers/cards-view-renderer.component.d.ts.map +1 -1
- package/dist/lib/view-types/renderers/cards-view-renderer.component.js +33 -5
- package/dist/lib/view-types/renderers/cards-view-renderer.component.js.map +1 -1
- package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts +160 -5
- package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts.map +1 -1
- package/dist/lib/view-types/renderers/grid-view-renderer.component.js +576 -36
- package/dist/lib/view-types/renderers/grid-view-renderer.component.js.map +1 -1
- package/dist/lib/view-types/renderers/map-view-renderer.component.d.ts +16 -2
- package/dist/lib/view-types/renderers/map-view-renderer.component.d.ts.map +1 -1
- package/dist/lib/view-types/renderers/map-view-renderer.component.js +37 -9
- package/dist/lib/view-types/renderers/map-view-renderer.component.js.map +1 -1
- package/dist/lib/view-types/view-type.contracts.js.map +1 -1
- package/dist/lib/view-workspace/view-workspace.component.d.ts +235 -24
- package/dist/lib/view-workspace/view-workspace.component.d.ts.map +1 -1
- package/dist/lib/view-workspace/view-workspace.component.js +600 -174
- package/dist/lib/view-workspace/view-workspace.component.js.map +1 -1
- package/dist/module.d.ts +5 -4
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +8 -1
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +1 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +1 -0
- package/dist/public-api.js.map +1 -1
- 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
|
-
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
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"]}
|