@memberjunction/ng-ui-components 5.34.1 → 5.35.0

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 (54) hide show
  1. package/dist/lib/filter-chip/filter-chip.component.d.ts +31 -0
  2. package/dist/lib/filter-chip/filter-chip.component.d.ts.map +1 -0
  3. package/dist/lib/filter-chip/filter-chip.component.js +94 -0
  4. package/dist/lib/filter-chip/filter-chip.component.js.map +1 -0
  5. package/dist/lib/filter-field/filter-field.component.d.ts +22 -0
  6. package/dist/lib/filter-field/filter-field.component.d.ts.map +1 -0
  7. package/dist/lib/filter-field/filter-field.component.js +60 -0
  8. package/dist/lib/filter-field/filter-field.component.js.map +1 -0
  9. package/dist/lib/filter-panel/filter-panel.component.d.ts +69 -0
  10. package/dist/lib/filter-panel/filter-panel.component.d.ts.map +1 -0
  11. package/dist/lib/filter-panel/filter-panel.component.js +203 -0
  12. package/dist/lib/filter-panel/filter-panel.component.js.map +1 -0
  13. package/dist/lib/filter-popover/filter-popover.component.d.ts +38 -0
  14. package/dist/lib/filter-popover/filter-popover.component.d.ts.map +1 -0
  15. package/dist/lib/filter-popover/filter-popover.component.js +170 -0
  16. package/dist/lib/filter-popover/filter-popover.component.js.map +1 -0
  17. package/dist/lib/page-body/page-body.component.d.ts +46 -0
  18. package/dist/lib/page-body/page-body.component.d.ts.map +1 -0
  19. package/dist/lib/page-body/page-body.component.js +72 -0
  20. package/dist/lib/page-body/page-body.component.js.map +1 -0
  21. package/dist/lib/page-header/page-header.component.d.ts +35 -0
  22. package/dist/lib/page-header/page-header.component.d.ts.map +1 -0
  23. package/dist/lib/page-header/page-header.component.js +121 -0
  24. package/dist/lib/page-header/page-header.component.js.map +1 -0
  25. package/dist/lib/page-header/page-header.scss +139 -0
  26. package/dist/lib/page-layout/page-layout.component.d.ts +20 -0
  27. package/dist/lib/page-layout/page-layout.component.d.ts.map +1 -0
  28. package/dist/lib/page-layout/page-layout.component.js +30 -0
  29. package/dist/lib/page-layout/page-layout.component.js.map +1 -0
  30. package/dist/lib/page-search/page-search.component.d.ts +29 -0
  31. package/dist/lib/page-search/page-search.component.d.ts.map +1 -0
  32. package/dist/lib/page-search/page-search.component.js +69 -0
  33. package/dist/lib/page-search/page-search.component.js.map +1 -0
  34. package/dist/lib/refresh-button/refresh-button.component.d.ts +46 -0
  35. package/dist/lib/refresh-button/refresh-button.component.d.ts.map +1 -0
  36. package/dist/lib/refresh-button/refresh-button.component.js +106 -0
  37. package/dist/lib/refresh-button/refresh-button.component.js.map +1 -0
  38. package/dist/lib/stat-badge/stat-badge.component.d.ts +54 -0
  39. package/dist/lib/stat-badge/stat-badge.component.d.ts.map +1 -0
  40. package/dist/lib/stat-badge/stat-badge.component.js +162 -0
  41. package/dist/lib/stat-badge/stat-badge.component.js.map +1 -0
  42. package/dist/lib/tab-nav/tab-nav.component.d.ts +41 -0
  43. package/dist/lib/tab-nav/tab-nav.component.d.ts.map +1 -0
  44. package/dist/lib/tab-nav/tab-nav.component.js +111 -0
  45. package/dist/lib/tab-nav/tab-nav.component.js.map +1 -0
  46. package/dist/lib/view-toggle/view-toggle.component.d.ts +42 -0
  47. package/dist/lib/view-toggle/view-toggle.component.d.ts.map +1 -0
  48. package/dist/lib/view-toggle/view-toggle.component.js +104 -0
  49. package/dist/lib/view-toggle/view-toggle.component.js.map +1 -0
  50. package/dist/public-api.d.ts +12 -0
  51. package/dist/public-api.d.ts.map +1 -1
  52. package/dist/public-api.js +12 -0
  53. package/dist/public-api.js.map +1 -1
  54. package/package.json +2 -2
@@ -0,0 +1,170 @@
1
+ import { Component, EventEmitter, Input, Output } from '@angular/core';
2
+ import { OverlayModule } from '@angular/cdk/overlay';
3
+ import * as i0 from "@angular/core";
4
+ import * as i1 from "@angular/cdk/overlay";
5
+ const _c0 = ["*"];
6
+ function MJFilterPopoverComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
7
+ i0.ɵɵelementStart(0, "span", 2);
8
+ i0.ɵɵtext(1);
9
+ i0.ɵɵelementEnd();
10
+ } if (rf & 2) {
11
+ const ctx_r1 = i0.ɵɵnextContext();
12
+ i0.ɵɵattribute("aria-label", ctx_r1.ActiveCount + " active filters");
13
+ i0.ɵɵadvance();
14
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.ActiveCount, " ");
15
+ } }
16
+ function MJFilterPopoverComponent_ng_template_7_Conditional_4_Template(rf, ctx) { if (rf & 1) {
17
+ const _r3 = i0.ɵɵgetCurrentView();
18
+ i0.ɵɵelementStart(0, "button", 10);
19
+ i0.ɵɵlistener("click", function MJFilterPopoverComponent_ng_template_7_Conditional_4_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.ClearAll()); });
20
+ i0.ɵɵtext(1, " Clear all ");
21
+ i0.ɵɵelementEnd();
22
+ } }
23
+ function MJFilterPopoverComponent_ng_template_7_Template(rf, ctx) { if (rf & 1) {
24
+ i0.ɵɵelementStart(0, "div", 5)(1, "div", 6)(2, "span", 7);
25
+ i0.ɵɵtext(3);
26
+ i0.ɵɵelementEnd();
27
+ i0.ɵɵconditionalCreate(4, MJFilterPopoverComponent_ng_template_7_Conditional_4_Template, 2, 0, "button", 8);
28
+ i0.ɵɵelementEnd();
29
+ i0.ɵɵelementStart(5, "div", 9);
30
+ i0.ɵɵprojection(6);
31
+ i0.ɵɵelementEnd()();
32
+ } if (rf & 2) {
33
+ const ctx_r1 = i0.ɵɵnextContext();
34
+ i0.ɵɵattribute("aria-label", ctx_r1.Label);
35
+ i0.ɵɵadvance(3);
36
+ i0.ɵɵtextInterpolate(ctx_r1.Label);
37
+ i0.ɵɵadvance();
38
+ i0.ɵɵconditional(ctx_r1.ActiveCount > 0 && ctx_r1.ShowClearAll ? 4 : -1);
39
+ } }
40
+ /**
41
+ * mj-filter-popover — Compact "Filters" trigger button that opens a popover
42
+ * containing arbitrary filter UI. Use this in a dashboard's page-header to
43
+ * keep the header chrome consistent regardless of filter density.
44
+ *
45
+ * Pass the popover content via `<ng-content>`. Drive the active-count badge
46
+ * via the `ActiveCount` input.
47
+ *
48
+ * Example:
49
+ * ```html
50
+ * <mj-filter-popover [ActiveCount]="activeFilters.length">
51
+ * <div class="my-filter-grid">
52
+ * <mj-dropdown ...></mj-dropdown>
53
+ * <mj-dropdown ...></mj-dropdown>
54
+ * </div>
55
+ * </mj-filter-popover>
56
+ * ```
57
+ */
58
+ export class MJFilterPopoverComponent {
59
+ Label = 'Filters';
60
+ Icon = 'fa-solid fa-filter';
61
+ ActiveCount = 0;
62
+ ShowClearAll = false;
63
+ IsOpen = false;
64
+ // Note: NO offsetY here — CDK applies offsetY as `transform: translateY(...)` on the
65
+ // overlay pane, which creates a containing block for `position: fixed` descendants.
66
+ // Tree dropdowns (and any other component using position: fixed) would then mis-position.
67
+ // The 6px spacing below the trigger is applied via margin-top on .mj-filter-popover-panel.
68
+ Positions = [
69
+ { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top' },
70
+ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top' },
71
+ { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom' }
72
+ ];
73
+ Toggle() { this.IsOpen = !this.IsOpen; }
74
+ Open() { this.IsOpen = true; }
75
+ Close() { this.IsOpen = false; }
76
+ /** Emitted when the user clicks the "Clear all" link inside the popover header. */
77
+ ClearAllRequested = new EventEmitter();
78
+ ClearAll() {
79
+ this.ClearAllRequested.emit();
80
+ }
81
+ static ɵfac = function MJFilterPopoverComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MJFilterPopoverComponent)(); };
82
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJFilterPopoverComponent, selectors: [["mj-filter-popover"]], inputs: { Label: "Label", Icon: "Icon", ActiveCount: "ActiveCount", ShowClearAll: "ShowClearAll" }, outputs: { ClearAllRequested: "ClearAllRequested" }, ngContentSelectors: _c0, decls: 8, vars: 14, consts: [["overlayOrigin", "cdkOverlayOrigin"], ["type", "button", "cdkOverlayOrigin", "", 1, "mj-filter-popover-trigger", 3, "click"], [1, "mj-filter-popover-badge"], [1, "fa-solid", "fa-chevron-down", "mj-filter-popover-chevron"], ["cdkConnectedOverlay", "", "cdkConnectedOverlayBackdropClass", "mj-filter-popover-backdrop", 3, "backdropClick", "detach", "cdkConnectedOverlayOrigin", "cdkConnectedOverlayOpen", "cdkConnectedOverlayPositions", "cdkConnectedOverlayHasBackdrop"], ["role", "dialog", 1, "mj-filter-popover-panel"], [1, "mj-filter-popover-header"], [1, "mj-filter-popover-title"], ["type", "button", 1, "mj-filter-popover-clear"], [1, "mj-filter-popover-content"], ["type", "button", 1, "mj-filter-popover-clear", 3, "click"]], template: function MJFilterPopoverComponent_Template(rf, ctx) { if (rf & 1) {
83
+ const _r1 = i0.ɵɵgetCurrentView();
84
+ i0.ɵɵprojectionDef();
85
+ i0.ɵɵelementStart(0, "button", 1, 0);
86
+ i0.ɵɵlistener("click", function MJFilterPopoverComponent_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.Toggle()); });
87
+ i0.ɵɵelement(2, "i");
88
+ i0.ɵɵelementStart(3, "span");
89
+ i0.ɵɵtext(4);
90
+ i0.ɵɵelementEnd();
91
+ i0.ɵɵconditionalCreate(5, MJFilterPopoverComponent_Conditional_5_Template, 2, 2, "span", 2);
92
+ i0.ɵɵelement(6, "i", 3);
93
+ i0.ɵɵelementEnd();
94
+ i0.ɵɵtemplate(7, MJFilterPopoverComponent_ng_template_7_Template, 7, 3, "ng-template", 4);
95
+ i0.ɵɵlistener("backdropClick", function MJFilterPopoverComponent_Template_ng_template_backdropClick_7_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.Close()); })("detach", function MJFilterPopoverComponent_Template_ng_template_detach_7_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.Close()); });
96
+ } if (rf & 2) {
97
+ const overlayOrigin_r4 = i0.ɵɵreference(1);
98
+ i0.ɵɵclassProp("mj-filter-popover-trigger--active", ctx.IsOpen || ctx.ActiveCount > 0);
99
+ i0.ɵɵattribute("aria-expanded", ctx.IsOpen)("aria-haspopup", true);
100
+ i0.ɵɵadvance(2);
101
+ i0.ɵɵclassMap(ctx.Icon);
102
+ i0.ɵɵadvance(2);
103
+ i0.ɵɵtextInterpolate(ctx.Label);
104
+ i0.ɵɵadvance();
105
+ i0.ɵɵconditional(ctx.ActiveCount > 0 ? 5 : -1);
106
+ i0.ɵɵadvance();
107
+ i0.ɵɵclassProp("mj-filter-popover-chevron--open", ctx.IsOpen);
108
+ i0.ɵɵadvance();
109
+ i0.ɵɵproperty("cdkConnectedOverlayOrigin", overlayOrigin_r4)("cdkConnectedOverlayOpen", ctx.IsOpen)("cdkConnectedOverlayPositions", ctx.Positions)("cdkConnectedOverlayHasBackdrop", true);
110
+ } }, dependencies: [OverlayModule, i1.CdkConnectedOverlay, i1.CdkOverlayOrigin], styles: ["[_nghost-%COMP%] {\n display: inline-flex;\n }\n\n .mj-filter-popover-trigger[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: var(--mj-space-2);\n padding: var(--mj-space-2) var(--mj-space-3-5);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n color: var(--mj-text-secondary);\n font-family: inherit;\n font-size: 13px;\n font-weight: 500;\n line-height: 1.2;\n cursor: pointer;\n transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;\n white-space: nowrap;\n }\n\n .mj-filter-popover-trigger[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-border-strong);\n color: var(--mj-text-primary);\n }\n\n .mj-filter-popover-trigger--active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-border-default));\n color: var(--mj-brand-primary);\n }\n\n .mj-filter-popover-trigger--active[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface));\n }\n\n .mj-filter-popover-trigger[_ngcontent-%COMP%] > i[_ngcontent-%COMP%]:first-child {\n font-size: var(--mj-text-xs);\n color: var(--mj-brand-primary);\n }\n\n .mj-filter-popover-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 20px;\n height: 20px;\n padding: 0 var(--mj-space-1-5);\n border-radius: 10px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 11px;\n font-weight: 700;\n line-height: 1;\n }\n\n .mj-filter-popover-chevron[_ngcontent-%COMP%] {\n font-size: 10px;\n transition: transform 0.2s ease;\n color: var(--mj-text-muted);\n }\n\n .mj-filter-popover-chevron--open[_ngcontent-%COMP%] {\n transform: rotate(180deg);\n }\n\n .mj-filter-popover-panel[_ngcontent-%COMP%] {\n min-width: 320px;\n max-width: 480px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-md);\n margin-top: var(--mj-space-1-5);\n overflow: hidden;\n }\n\n .mj-filter-popover-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--mj-space-3) var(--mj-space-4) 0;\n }\n\n .mj-filter-popover-title[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n\n .mj-filter-popover-clear[_ngcontent-%COMP%] {\n background: transparent;\n border: none;\n color: var(--mj-brand-primary);\n font-size: var(--mj-text-xs);\n font-weight: 500;\n cursor: pointer;\n padding: var(--mj-space-1) var(--mj-space-2);\n border-radius: var(--mj-radius-sm);\n }\n\n .mj-filter-popover-clear[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n }\n\n .mj-filter-popover-content[_ngcontent-%COMP%] {\n padding: var(--mj-space-3) var(--mj-space-4) var(--mj-space-4);\n display: flex;\n flex-direction: column;\n gap: var(--mj-space-2-5);\n }"] });
111
+ }
112
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJFilterPopoverComponent, [{
113
+ type: Component,
114
+ args: [{ selector: 'mj-filter-popover', standalone: true, imports: [OverlayModule], template: `
115
+ <button
116
+ type="button"
117
+ class="mj-filter-popover-trigger"
118
+ [class.mj-filter-popover-trigger--active]="IsOpen || ActiveCount > 0"
119
+ cdkOverlayOrigin
120
+ #overlayOrigin="cdkOverlayOrigin"
121
+ [attr.aria-expanded]="IsOpen"
122
+ [attr.aria-haspopup]="true"
123
+ (click)="Toggle()">
124
+ <i [class]="Icon"></i>
125
+ <span>{{ Label }}</span>
126
+ @if (ActiveCount > 0) {
127
+ <span class="mj-filter-popover-badge" [attr.aria-label]="ActiveCount + ' active filters'">
128
+ {{ ActiveCount }}
129
+ </span>
130
+ }
131
+ <i class="fa-solid fa-chevron-down mj-filter-popover-chevron" [class.mj-filter-popover-chevron--open]="IsOpen"></i>
132
+ </button>
133
+
134
+ <ng-template
135
+ cdkConnectedOverlay
136
+ [cdkConnectedOverlayOrigin]="overlayOrigin"
137
+ [cdkConnectedOverlayOpen]="IsOpen"
138
+ [cdkConnectedOverlayPositions]="Positions"
139
+ [cdkConnectedOverlayHasBackdrop]="true"
140
+ cdkConnectedOverlayBackdropClass="mj-filter-popover-backdrop"
141
+ (backdropClick)="Close()"
142
+ (detach)="Close()">
143
+ <div class="mj-filter-popover-panel" role="dialog" [attr.aria-label]="Label">
144
+ <div class="mj-filter-popover-header">
145
+ <span class="mj-filter-popover-title">{{ Label }}</span>
146
+ @if (ActiveCount > 0 && ShowClearAll) {
147
+ <button type="button" class="mj-filter-popover-clear" (click)="ClearAll()">
148
+ Clear all
149
+ </button>
150
+ }
151
+ </div>
152
+ <div class="mj-filter-popover-content">
153
+ <ng-content></ng-content>
154
+ </div>
155
+ </div>
156
+ </ng-template>
157
+ `, styles: ["\n :host {\n display: inline-flex;\n }\n\n .mj-filter-popover-trigger {\n display: inline-flex;\n align-items: center;\n gap: var(--mj-space-2);\n padding: var(--mj-space-2) var(--mj-space-3-5);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n color: var(--mj-text-secondary);\n font-family: inherit;\n font-size: 13px;\n font-weight: 500;\n line-height: 1.2;\n cursor: pointer;\n transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;\n white-space: nowrap;\n }\n\n .mj-filter-popover-trigger:hover {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-border-strong);\n color: var(--mj-text-primary);\n }\n\n .mj-filter-popover-trigger--active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-border-default));\n color: var(--mj-brand-primary);\n }\n\n .mj-filter-popover-trigger--active:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface));\n }\n\n .mj-filter-popover-trigger > i:first-child {\n font-size: var(--mj-text-xs);\n color: var(--mj-brand-primary);\n }\n\n .mj-filter-popover-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 20px;\n height: 20px;\n padding: 0 var(--mj-space-1-5);\n border-radius: 10px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 11px;\n font-weight: 700;\n line-height: 1;\n }\n\n .mj-filter-popover-chevron {\n font-size: 10px;\n transition: transform 0.2s ease;\n color: var(--mj-text-muted);\n }\n\n .mj-filter-popover-chevron--open {\n transform: rotate(180deg);\n }\n\n .mj-filter-popover-panel {\n min-width: 320px;\n max-width: 480px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-md);\n margin-top: var(--mj-space-1-5);\n overflow: hidden;\n }\n\n .mj-filter-popover-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--mj-space-3) var(--mj-space-4) 0;\n }\n\n .mj-filter-popover-title {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n\n .mj-filter-popover-clear {\n background: transparent;\n border: none;\n color: var(--mj-brand-primary);\n font-size: var(--mj-text-xs);\n font-weight: 500;\n cursor: pointer;\n padding: var(--mj-space-1) var(--mj-space-2);\n border-radius: var(--mj-radius-sm);\n }\n\n .mj-filter-popover-clear:hover {\n background: var(--mj-bg-surface-hover);\n }\n\n .mj-filter-popover-content {\n padding: var(--mj-space-3) var(--mj-space-4) var(--mj-space-4);\n display: flex;\n flex-direction: column;\n gap: var(--mj-space-2-5);\n }\n "] }]
158
+ }], null, { Label: [{
159
+ type: Input
160
+ }], Icon: [{
161
+ type: Input
162
+ }], ActiveCount: [{
163
+ type: Input
164
+ }], ShowClearAll: [{
165
+ type: Input
166
+ }], ClearAllRequested: [{
167
+ type: Output
168
+ }] }); })();
169
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJFilterPopoverComponent, { className: "MJFilterPopoverComponent", filePath: "lib/filter-popover/filter-popover.component.ts", lineNumber: 186 }); })();
170
+ //# sourceMappingURL=filter-popover.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-popover.component.js","sourceRoot":"","sources":["../../../src/lib/filter-popover/filter-popover.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,aAAa,EAAqB,MAAM,sBAAsB,CAAC;;;;;IAqChE,+BAA0F;IACxF,YACF;IAAA,iBAAO;;;;IADL,cACF;IADE,mDACF;;;;IAkBI,kCAA2E;IAArB,2MAAS,iBAAU,KAAC;IACxE,2BACF;IAAA,iBAAS;;;IAJX,AADF,AADF,8BAA6E,aACrC,cACE;IAAA,YAAW;IAAA,iBAAO;IACxD,2GAAuC;IAKzC,iBAAM;IACN,8BAAuC;IACrC,kBAAyB;IAE7B,AADE,iBAAM,EACF;;;;IAVoC,eAAW;IAAX,kCAAW;IACjD,cAIC;IAJD,wEAIC;;AA1DX;;;;;;;;;;;;;;;;;GAiBG;AAqKH,MAAM,OAAO,wBAAwB;IAC1B,KAAK,GAAW,SAAS,CAAC;IAC1B,IAAI,GAAW,oBAAoB,CAAC;IACpC,WAAW,GAAW,CAAC,CAAC;IACxB,YAAY,GAAY,KAAK,CAAC;IAEhC,MAAM,GAAY,KAAK,CAAC;IAE/B,qFAAqF;IACrF,oFAAoF;IACpF,0FAA0F;IAC1F,2FAA2F;IAC3E,SAAS,GAAwB;QAC/C,EAAE,OAAO,EAAE,KAAK,EAAI,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAI,QAAQ,EAAE,KAAK,EAAE;QAC3E,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC3E,EAAE,OAAO,EAAE,KAAK,EAAI,OAAO,EAAE,KAAK,EAAK,QAAQ,EAAE,KAAK,EAAI,QAAQ,EAAE,QAAQ,EAAE;KAC/E,CAAC;IAEK,MAAM,KAAW,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9C,IAAI,KAAW,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IACpC,KAAK,KAAW,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC;IAE7C,mFAAmF;IAClE,iBAAiB,GAAG,IAAI,YAAY,EAAQ,CAAC;IAEvD,QAAQ;QACb,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC;IAChC,CAAC;kHA3BU,wBAAwB;6DAAxB,wBAAwB;;;YA/JjC,oCAQqB;YAAnB,2IAAS,YAAQ,KAAC;YAClB,oBAAsB;YACtB,4BAAM;YAAA,YAAW;YAAA,iBAAO;YACxB,2FAAuB;YAKvB,uBAAmH;YACrH,iBAAS;YAET,yFAQqB;YAAnB,AADA,gKAAiB,WAAO,KAAC,qIACf,WAAO,KAAC;;;YAxBlB,sFAAqE;;YAMlE,eAAc;YAAd,uBAAc;YACX,eAAW;YAAX,+BAAW;YACjB,cAIC;YAJD,8CAIC;YAC6D,cAAgD;YAAhD,6DAAgD;YAK9G,cAA2C;YAG3C,AADA,AADA,AADA,4DAA2C,uCACT,+CACQ,wCACH;4BA1BjC,aAAa;;iFAiKZ,wBAAwB;cApKpC,SAAS;2BACE,mBAAmB,cACjB,IAAI,WACP,CAAC,aAAa,CAAC,YACd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CT;;kBAsHA,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBAmBL,MAAM;;kFAvBI,wBAAwB","sourcesContent":["import { Component, EventEmitter, Input, Output } from '@angular/core';\nimport { OverlayModule, ConnectedPosition } from '@angular/cdk/overlay';\n\n/**\n * mj-filter-popover — Compact \"Filters\" trigger button that opens a popover\n * containing arbitrary filter UI. Use this in a dashboard's page-header to\n * keep the header chrome consistent regardless of filter density.\n *\n * Pass the popover content via `<ng-content>`. Drive the active-count badge\n * via the `ActiveCount` input.\n *\n * Example:\n * ```html\n * <mj-filter-popover [ActiveCount]=\"activeFilters.length\">\n * <div class=\"my-filter-grid\">\n * <mj-dropdown ...></mj-dropdown>\n * <mj-dropdown ...></mj-dropdown>\n * </div>\n * </mj-filter-popover>\n * ```\n */\n@Component({\n selector: 'mj-filter-popover',\n standalone: true,\n imports: [OverlayModule],\n template: `\n <button\n type=\"button\"\n class=\"mj-filter-popover-trigger\"\n [class.mj-filter-popover-trigger--active]=\"IsOpen || ActiveCount > 0\"\n cdkOverlayOrigin\n #overlayOrigin=\"cdkOverlayOrigin\"\n [attr.aria-expanded]=\"IsOpen\"\n [attr.aria-haspopup]=\"true\"\n (click)=\"Toggle()\">\n <i [class]=\"Icon\"></i>\n <span>{{ Label }}</span>\n @if (ActiveCount > 0) {\n <span class=\"mj-filter-popover-badge\" [attr.aria-label]=\"ActiveCount + ' active filters'\">\n {{ ActiveCount }}\n </span>\n }\n <i class=\"fa-solid fa-chevron-down mj-filter-popover-chevron\" [class.mj-filter-popover-chevron--open]=\"IsOpen\"></i>\n </button>\n\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"overlayOrigin\"\n [cdkConnectedOverlayOpen]=\"IsOpen\"\n [cdkConnectedOverlayPositions]=\"Positions\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"mj-filter-popover-backdrop\"\n (backdropClick)=\"Close()\"\n (detach)=\"Close()\">\n <div class=\"mj-filter-popover-panel\" role=\"dialog\" [attr.aria-label]=\"Label\">\n <div class=\"mj-filter-popover-header\">\n <span class=\"mj-filter-popover-title\">{{ Label }}</span>\n @if (ActiveCount > 0 && ShowClearAll) {\n <button type=\"button\" class=\"mj-filter-popover-clear\" (click)=\"ClearAll()\">\n Clear all\n </button>\n }\n </div>\n <div class=\"mj-filter-popover-content\">\n <ng-content></ng-content>\n </div>\n </div>\n </ng-template>\n `,\n styles: [`\n :host {\n display: inline-flex;\n }\n\n .mj-filter-popover-trigger {\n display: inline-flex;\n align-items: center;\n gap: var(--mj-space-2);\n padding: var(--mj-space-2) var(--mj-space-3-5);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n color: var(--mj-text-secondary);\n font-family: inherit;\n font-size: 13px;\n font-weight: 500;\n line-height: 1.2;\n cursor: pointer;\n transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;\n white-space: nowrap;\n }\n\n .mj-filter-popover-trigger:hover {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-border-strong);\n color: var(--mj-text-primary);\n }\n\n .mj-filter-popover-trigger--active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-border-default));\n color: var(--mj-brand-primary);\n }\n\n .mj-filter-popover-trigger--active:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface));\n }\n\n .mj-filter-popover-trigger > i:first-child {\n font-size: var(--mj-text-xs);\n color: var(--mj-brand-primary);\n }\n\n .mj-filter-popover-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 20px;\n height: 20px;\n padding: 0 var(--mj-space-1-5);\n border-radius: 10px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 11px;\n font-weight: 700;\n line-height: 1;\n }\n\n .mj-filter-popover-chevron {\n font-size: 10px;\n transition: transform 0.2s ease;\n color: var(--mj-text-muted);\n }\n\n .mj-filter-popover-chevron--open {\n transform: rotate(180deg);\n }\n\n .mj-filter-popover-panel {\n min-width: 320px;\n max-width: 480px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-md);\n margin-top: var(--mj-space-1-5);\n overflow: hidden;\n }\n\n .mj-filter-popover-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--mj-space-3) var(--mj-space-4) 0;\n }\n\n .mj-filter-popover-title {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n\n .mj-filter-popover-clear {\n background: transparent;\n border: none;\n color: var(--mj-brand-primary);\n font-size: var(--mj-text-xs);\n font-weight: 500;\n cursor: pointer;\n padding: var(--mj-space-1) var(--mj-space-2);\n border-radius: var(--mj-radius-sm);\n }\n\n .mj-filter-popover-clear:hover {\n background: var(--mj-bg-surface-hover);\n }\n\n .mj-filter-popover-content {\n padding: var(--mj-space-3) var(--mj-space-4) var(--mj-space-4);\n display: flex;\n flex-direction: column;\n gap: var(--mj-space-2-5);\n }\n `]\n})\nexport class MJFilterPopoverComponent {\n @Input() Label: string = 'Filters';\n @Input() Icon: string = 'fa-solid fa-filter';\n @Input() ActiveCount: number = 0;\n @Input() ShowClearAll: boolean = false;\n\n public IsOpen: boolean = false;\n\n // Note: NO offsetY here — CDK applies offsetY as `transform: translateY(...)` on the\n // overlay pane, which creates a containing block for `position: fixed` descendants.\n // Tree dropdowns (and any other component using position: fixed) would then mis-position.\n // The 6px spacing below the trigger is applied via margin-top on .mj-filter-popover-panel.\n public readonly Positions: ConnectedPosition[] = [\n { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top' },\n { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top' },\n { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom' }\n ];\n\n public Toggle(): void { this.IsOpen = !this.IsOpen; }\n public Open(): void { this.IsOpen = true; }\n public Close(): void { this.IsOpen = false; }\n\n /** Emitted when the user clicks the \"Clear all\" link inside the popover header. */\n @Output() public ClearAllRequested = new EventEmitter<void>();\n\n public ClearAll(): void {\n this.ClearAllRequested.emit();\n }\n}\n"]}
@@ -0,0 +1,46 @@
1
+ import * as i0 from "@angular/core";
2
+ /**
3
+ * mj-page-body — Standard body region for MJ Explorer dashboards / resource pages.
4
+ *
5
+ * Sits inside `<mj-page-layout>` after `<mj-page-header>`. Provides the canonical
6
+ * page body recipe: `flex: 1`, `min-height: 0`, padded gutters, vertical scroll.
7
+ *
8
+ * Escape hatches:
9
+ * - `[Padding]="false"` — remove the 24px gutter (e.g., pages whose inner
10
+ * content owns the gutter itself, like File Browser or AI Analytics shell).
11
+ * - `[Flex]="true"` — switch the body to `flex; flex-direction: column;
12
+ * position: relative` so a child marked `flex: 1` (e.g., a main content area
13
+ * that should fill remaining height under a banner row) can grow correctly.
14
+ *
15
+ * Note: overflow-y is intentionally always `auto` and not configurable.
16
+ * `mj-page-layout` already has `overflow: hidden`, so any body content that
17
+ * exceeds the visible area would be silently clipped if the body itself didn't
18
+ * scroll. Pages with split-pane layouts whose children manage their own scroll
19
+ * regions should use `min-height: 0 + flex: 1` on the inner panes so nothing
20
+ * overflows the body — auto stays harmlessly inactive.
21
+ *
22
+ * Example:
23
+ * ```html
24
+ * <mj-page-layout>
25
+ * <mj-page-header Title="Jobs" Icon="fa-solid fa-calendar-check"></mj-page-header>
26
+ * <mj-page-body>
27
+ * <!-- page content -->
28
+ * </mj-page-body>
29
+ * </mj-page-layout>
30
+ * ```
31
+ */
32
+ export declare class MJPageBodyComponent {
33
+ /** When `false`, removes the default 24px gutter. Defaults to `true`. */
34
+ Padding: boolean;
35
+ /**
36
+ * When `true`, switches the body to flex-column layout (with `position: relative`)
37
+ * so a child marked `flex: 1` can fill the remaining vertical space — e.g., a main
38
+ * content area sitting under a banner row. Defaults to `false` (block flow).
39
+ */
40
+ Flex: boolean;
41
+ get NoPaddingClass(): boolean;
42
+ get FlexClass(): boolean;
43
+ static ɵfac: i0.ɵɵFactoryDeclaration<MJPageBodyComponent, never>;
44
+ static ɵcmp: i0.ɵɵComponentDeclaration<MJPageBodyComponent, "mj-page-body", never, { "Padding": { "alias": "Padding"; "required": false; }; "Flex": { "alias": "Flex"; "required": false; }; }, {}, never, ["*"], true, never>;
45
+ }
46
+ //# sourceMappingURL=page-body.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-body.component.d.ts","sourceRoot":"","sources":["../../../src/lib/page-body/page-body.component.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAyBa,mBAAmB;IAC9B,yEAAyE;IAChE,OAAO,EAAE,OAAO,CAAQ;IAEjC;;;;OAIG;IACM,IAAI,EAAE,OAAO,CAAS;IAE/B,IACI,cAAc,IAAI,OAAO,CAE5B;IAED,IACI,SAAS,IAAI,OAAO,CAEvB;yCAnBU,mBAAmB;2CAAnB,mBAAmB;CAoB/B"}
@@ -0,0 +1,72 @@
1
+ import { Component, HostBinding, Input } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ const _c0 = ["*"];
4
+ /**
5
+ * mj-page-body — Standard body region for MJ Explorer dashboards / resource pages.
6
+ *
7
+ * Sits inside `<mj-page-layout>` after `<mj-page-header>`. Provides the canonical
8
+ * page body recipe: `flex: 1`, `min-height: 0`, padded gutters, vertical scroll.
9
+ *
10
+ * Escape hatches:
11
+ * - `[Padding]="false"` — remove the 24px gutter (e.g., pages whose inner
12
+ * content owns the gutter itself, like File Browser or AI Analytics shell).
13
+ * - `[Flex]="true"` — switch the body to `flex; flex-direction: column;
14
+ * position: relative` so a child marked `flex: 1` (e.g., a main content area
15
+ * that should fill remaining height under a banner row) can grow correctly.
16
+ *
17
+ * Note: overflow-y is intentionally always `auto` and not configurable.
18
+ * `mj-page-layout` already has `overflow: hidden`, so any body content that
19
+ * exceeds the visible area would be silently clipped if the body itself didn't
20
+ * scroll. Pages with split-pane layouts whose children manage their own scroll
21
+ * regions should use `min-height: 0 + flex: 1` on the inner panes so nothing
22
+ * overflows the body — auto stays harmlessly inactive.
23
+ *
24
+ * Example:
25
+ * ```html
26
+ * <mj-page-layout>
27
+ * <mj-page-header Title="Jobs" Icon="fa-solid fa-calendar-check"></mj-page-header>
28
+ * <mj-page-body>
29
+ * <!-- page content -->
30
+ * </mj-page-body>
31
+ * </mj-page-layout>
32
+ * ```
33
+ */
34
+ export class MJPageBodyComponent {
35
+ /** When `false`, removes the default 24px gutter. Defaults to `true`. */
36
+ Padding = true;
37
+ /**
38
+ * When `true`, switches the body to flex-column layout (with `position: relative`)
39
+ * so a child marked `flex: 1` can fill the remaining vertical space — e.g., a main
40
+ * content area sitting under a banner row. Defaults to `false` (block flow).
41
+ */
42
+ Flex = false;
43
+ get NoPaddingClass() {
44
+ return !this.Padding;
45
+ }
46
+ get FlexClass() {
47
+ return this.Flex;
48
+ }
49
+ static ɵfac = function MJPageBodyComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MJPageBodyComponent)(); };
50
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJPageBodyComponent, selectors: [["mj-page-body"]], hostVars: 4, hostBindings: function MJPageBodyComponent_HostBindings(rf, ctx) { if (rf & 2) {
51
+ i0.ɵɵclassProp("mj-page-body--no-padding", ctx.NoPaddingClass)("mj-page-body--flex", ctx.FlexClass);
52
+ } }, inputs: { Padding: "Padding", Flex: "Flex" }, ngContentSelectors: _c0, decls: 1, vars: 0, template: function MJPageBodyComponent_Template(rf, ctx) { if (rf & 1) {
53
+ i0.ɵɵprojectionDef();
54
+ i0.ɵɵprojection(0);
55
+ } }, styles: ["[_nghost-%COMP%] {\n display: block;\n flex: 1;\n min-height: 0;\n \n\n\n\n padding: var(--mj-space-6);\n overflow-y: auto;\n }\n .mj-page-body--no-padding[_nghost-%COMP%] {\n padding: 0;\n }\n .mj-page-body--flex[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n position: relative;\n }"] });
56
+ }
57
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJPageBodyComponent, [{
58
+ type: Component,
59
+ args: [{ selector: 'mj-page-body', standalone: true, template: `<ng-content></ng-content>`, styles: ["\n :host {\n display: block;\n flex: 1;\n min-height: 0;\n /* Top padding doubles as header-to-body spacing \u2014 <mj-page-header>\n intentionally has no margin-bottom so [Padding]=\"false\" yields a\n truly flush layout (e.g. AI Analytics sidebar+content). */\n padding: var(--mj-space-6);\n overflow-y: auto;\n }\n :host(.mj-page-body--no-padding) {\n padding: 0;\n }\n :host(.mj-page-body--flex) {\n display: flex;\n flex-direction: column;\n position: relative;\n }\n "] }]
60
+ }], null, { Padding: [{
61
+ type: Input
62
+ }], Flex: [{
63
+ type: Input
64
+ }], NoPaddingClass: [{
65
+ type: HostBinding,
66
+ args: ['class.mj-page-body--no-padding']
67
+ }], FlexClass: [{
68
+ type: HostBinding,
69
+ args: ['class.mj-page-body--flex']
70
+ }] }); })();
71
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJPageBodyComponent, { className: "MJPageBodyComponent", filePath: "lib/page-body/page-body.component.ts", lineNumber: 58 }); })();
72
+ //# sourceMappingURL=page-body.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-body.component.js","sourceRoot":"","sources":["../../../src/lib/page-body/page-body.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;;;AAE9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AA0BH,MAAM,OAAO,mBAAmB;IAC9B,yEAAyE;IAChE,OAAO,GAAY,IAAI,CAAC;IAEjC;;;;OAIG;IACM,IAAI,GAAY,KAAK,CAAC;IAE/B,IACI,cAAc;QAChB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;IACvB,CAAC;IAED,IACI,SAAS;QACX,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;6GAnBU,mBAAmB;6DAAnB,mBAAmB;YAAnB,8DAAmB,qCAAA;;;YAtBnB,kBAAyB;;;iFAsBzB,mBAAmB;cAzB/B,SAAS;2BACE,cAAc,cACZ,IAAI,YACN,2BAA2B;;kBAwBpC,KAAK;;kBAOL,KAAK;;kBAEL,WAAW;mBAAC,gCAAgC;;kBAK5C,WAAW;mBAAC,0BAA0B;;kFAhB5B,mBAAmB","sourcesContent":["import { Component, HostBinding, Input } from '@angular/core';\n\n/**\n * mj-page-body — Standard body region for MJ Explorer dashboards / resource pages.\n *\n * Sits inside `<mj-page-layout>` after `<mj-page-header>`. Provides the canonical\n * page body recipe: `flex: 1`, `min-height: 0`, padded gutters, vertical scroll.\n *\n * Escape hatches:\n * - `[Padding]=\"false\"` — remove the 24px gutter (e.g., pages whose inner\n * content owns the gutter itself, like File Browser or AI Analytics shell).\n * - `[Flex]=\"true\"` — switch the body to `flex; flex-direction: column;\n * position: relative` so a child marked `flex: 1` (e.g., a main content area\n * that should fill remaining height under a banner row) can grow correctly.\n *\n * Note: overflow-y is intentionally always `auto` and not configurable.\n * `mj-page-layout` already has `overflow: hidden`, so any body content that\n * exceeds the visible area would be silently clipped if the body itself didn't\n * scroll. Pages with split-pane layouts whose children manage their own scroll\n * regions should use `min-height: 0 + flex: 1` on the inner panes so nothing\n * overflows the body — auto stays harmlessly inactive.\n *\n * Example:\n * ```html\n * <mj-page-layout>\n * <mj-page-header Title=\"Jobs\" Icon=\"fa-solid fa-calendar-check\"></mj-page-header>\n * <mj-page-body>\n * <!-- page content -->\n * </mj-page-body>\n * </mj-page-layout>\n * ```\n */\n@Component({\n selector: 'mj-page-body',\n standalone: true,\n template: `<ng-content></ng-content>`,\n styles: [`\n :host {\n display: block;\n flex: 1;\n min-height: 0;\n /* Top padding doubles as header-to-body spacing — <mj-page-header>\n intentionally has no margin-bottom so [Padding]=\"false\" yields a\n truly flush layout (e.g. AI Analytics sidebar+content). */\n padding: var(--mj-space-6);\n overflow-y: auto;\n }\n :host(.mj-page-body--no-padding) {\n padding: 0;\n }\n :host(.mj-page-body--flex) {\n display: flex;\n flex-direction: column;\n position: relative;\n }\n `]\n})\nexport class MJPageBodyComponent {\n /** When `false`, removes the default 24px gutter. Defaults to `true`. */\n @Input() Padding: boolean = true;\n\n /**\n * When `true`, switches the body to flex-column layout (with `position: relative`)\n * so a child marked `flex: 1` can fill the remaining vertical space — e.g., a main\n * content area sitting under a banner row. Defaults to `false` (block flow).\n */\n @Input() Flex: boolean = false;\n\n @HostBinding('class.mj-page-body--no-padding')\n get NoPaddingClass(): boolean {\n return !this.Padding;\n }\n\n @HostBinding('class.mj-page-body--flex')\n get FlexClass(): boolean {\n return this.Flex;\n }\n}\n"]}
@@ -0,0 +1,35 @@
1
+ import * as i0 from "@angular/core";
2
+ /**
3
+ * mj-page-header — Canonical page/dashboard header for MJ Explorer.
4
+ *
5
+ * Renders the standard icon · title · subtitle layout with three named projection
6
+ * slots for additional content:
7
+ * - `[meta]` — inline content immediately after the title text (result counts,
8
+ * status badges) — wraps onto the title's row so it stays
9
+ * horizontally adjacent to the title regardless of subtitle.
10
+ * - `[actions]` — right-aligned action buttons or sub-navigation
11
+ * - `[toolbar]` — optional secondary row beneath the title for dense controls
12
+ * (filters, time-range pickers, breadcrumbs)
13
+ *
14
+ * Example:
15
+ * ```html
16
+ * <mj-page-header Title="Dashboard" Icon="fa-solid fa-gauge" Subtitle="Overview">
17
+ * <span meta class="badge">{{ count }}</span>
18
+ * <div actions>
19
+ * <button mjButton variant="primary">New</button>
20
+ * </div>
21
+ * <div toolbar>
22
+ * <app-time-range-picker></app-time-range-picker>
23
+ * <app-filter-bar></app-filter-bar>
24
+ * </div>
25
+ * </mj-page-header>
26
+ * ```
27
+ */
28
+ export declare class MJPageHeaderComponent {
29
+ Title: string;
30
+ Icon: string | null;
31
+ Subtitle: string | null;
32
+ static ɵfac: i0.ɵɵFactoryDeclaration<MJPageHeaderComponent, never>;
33
+ static ɵcmp: i0.ɵɵComponentDeclaration<MJPageHeaderComponent, "mj-page-header", never, { "Title": { "alias": "Title"; "required": false; }; "Icon": { "alias": "Icon"; "required": false; }; "Subtitle": { "alias": "Subtitle"; "required": false; }; }, {}, never, ["[meta]", "[actions]", "[toolbar]"], true, never>;
34
+ }
35
+ //# sourceMappingURL=page-header.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-header.component.d.ts","sourceRoot":"","sources":["../../../src/lib/page-header/page-header.component.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAmCa,qBAAqB;IACvB,KAAK,EAAE,MAAM,CAAM;IACnB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;yCAH7B,qBAAqB;2CAArB,qBAAqB;CAIjC"}
@@ -0,0 +1,121 @@
1
+ import { Component, Input } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ const _c0 = [[["", "meta", ""]], [["", "actions", ""]], [["", "toolbar", ""]]];
4
+ const _c1 = ["[meta]", "[actions]", "[toolbar]"];
5
+ function MJPageHeaderComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
6
+ i0.ɵɵdomElementStart(0, "div", 3);
7
+ i0.ɵɵdomElement(1, "i");
8
+ i0.ɵɵdomElementEnd();
9
+ } if (rf & 2) {
10
+ const ctx_r0 = i0.ɵɵnextContext();
11
+ i0.ɵɵadvance();
12
+ i0.ɵɵclassMap(ctx_r0.Icon);
13
+ } }
14
+ function MJPageHeaderComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
15
+ i0.ɵɵdomElementStart(0, "p", 8);
16
+ i0.ɵɵtext(1);
17
+ i0.ɵɵdomElementEnd();
18
+ } if (rf & 2) {
19
+ const ctx_r0 = i0.ɵɵnextContext();
20
+ i0.ɵɵadvance();
21
+ i0.ɵɵtextInterpolate(ctx_r0.Subtitle);
22
+ } }
23
+ /**
24
+ * mj-page-header — Canonical page/dashboard header for MJ Explorer.
25
+ *
26
+ * Renders the standard icon · title · subtitle layout with three named projection
27
+ * slots for additional content:
28
+ * - `[meta]` — inline content immediately after the title text (result counts,
29
+ * status badges) — wraps onto the title's row so it stays
30
+ * horizontally adjacent to the title regardless of subtitle.
31
+ * - `[actions]` — right-aligned action buttons or sub-navigation
32
+ * - `[toolbar]` — optional secondary row beneath the title for dense controls
33
+ * (filters, time-range pickers, breadcrumbs)
34
+ *
35
+ * Example:
36
+ * ```html
37
+ * <mj-page-header Title="Dashboard" Icon="fa-solid fa-gauge" Subtitle="Overview">
38
+ * <span meta class="badge">{{ count }}</span>
39
+ * <div actions>
40
+ * <button mjButton variant="primary">New</button>
41
+ * </div>
42
+ * <div toolbar>
43
+ * <app-time-range-picker></app-time-range-picker>
44
+ * <app-filter-bar></app-filter-bar>
45
+ * </div>
46
+ * </mj-page-header>
47
+ * ```
48
+ */
49
+ export class MJPageHeaderComponent {
50
+ Title = '';
51
+ Icon = null;
52
+ Subtitle = null;
53
+ static ɵfac = function MJPageHeaderComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MJPageHeaderComponent)(); };
54
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJPageHeaderComponent, selectors: [["mj-page-header"]], inputs: { Title: "Title", Icon: "Icon", Subtitle: "Subtitle" }, ngContentSelectors: _c1, decls: 15, vars: 3, consts: [[1, "mj-page-header"], [1, "mj-page-header-row", "mj-page-header-row--primary"], [1, "mj-page-header-titlebar"], ["aria-hidden", "true", 1, "mj-page-header-icon"], [1, "mj-page-header-text"], [1, "mj-page-header-title-row"], [1, "mj-page-header-title"], [1, "mj-page-header-meta"], [1, "mj-page-header-subtitle"], [1, "mj-page-header-actions"], [1, "mj-page-header-row", "mj-page-header-row--toolbar"]], template: function MJPageHeaderComponent_Template(rf, ctx) { if (rf & 1) {
55
+ i0.ɵɵprojectionDef(_c0);
56
+ i0.ɵɵdomElementStart(0, "header", 0)(1, "div", 1)(2, "div", 2);
57
+ i0.ɵɵconditionalCreate(3, MJPageHeaderComponent_Conditional_3_Template, 2, 2, "div", 3);
58
+ i0.ɵɵdomElementStart(4, "div", 4)(5, "div", 5)(6, "h1", 6);
59
+ i0.ɵɵtext(7);
60
+ i0.ɵɵdomElementEnd();
61
+ i0.ɵɵdomElementStart(8, "div", 7);
62
+ i0.ɵɵprojection(9);
63
+ i0.ɵɵdomElementEnd()();
64
+ i0.ɵɵconditionalCreate(10, MJPageHeaderComponent_Conditional_10_Template, 2, 1, "p", 8);
65
+ i0.ɵɵdomElementEnd()();
66
+ i0.ɵɵdomElementStart(11, "div", 9);
67
+ i0.ɵɵprojection(12, 1);
68
+ i0.ɵɵdomElementEnd()();
69
+ i0.ɵɵdomElementStart(13, "div", 10);
70
+ i0.ɵɵprojection(14, 2);
71
+ i0.ɵɵdomElementEnd()();
72
+ } if (rf & 2) {
73
+ i0.ɵɵadvance(3);
74
+ i0.ɵɵconditional(ctx.Icon ? 3 : -1);
75
+ i0.ɵɵadvance(4);
76
+ i0.ɵɵtextInterpolate(ctx.Title);
77
+ i0.ɵɵadvance(3);
78
+ i0.ɵɵconditional(ctx.Subtitle ? 10 : -1);
79
+ } }, styles: ["[_nghost-%COMP%] {\n display: block;\n \n\n\n\n margin-bottom: var(--mj-space-6);\n}\n\n\n\n\n\n\nmj-page-layout[_nghost-%COMP%], mj-page-layout [_nghost-%COMP%] {\n margin-bottom: 0;\n}\n\n\n\n\n\n\n\n\n[_nghost-%COMP%] [meta], \n[_nghost-%COMP%] [actions], \n[_nghost-%COMP%] [toolbar] {\n display: contents;\n}\n\n.mj-page-header[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.mj-page-header-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n padding: 0 var(--mj-space-6);\n}\n\n.mj-page-header-row--primary[_ngcontent-%COMP%] {\n justify-content: space-between;\n gap: var(--mj-space-4);\n padding-top: var(--mj-space-4);\n padding-bottom: var(--mj-space-4);\n}\n\n.mj-page-header-row--toolbar[_ngcontent-%COMP%] {\n flex-wrap: wrap;\n padding-top: 0;\n padding-bottom: var(--mj-space-4);\n border-top: 1px solid var(--mj-border-subtle);\n margin-top: 0;\n padding-top: var(--mj-space-3);\n}\n\n.mj-page-header-row--toolbar[_ngcontent-%COMP%]:empty {\n display: none;\n border-top: 0;\n padding: 0;\n}\n\n.mj-page-header-titlebar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.mj-page-header-icon[_ngcontent-%COMP%] {\n flex-shrink: 0;\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n color: var(--mj-brand-primary);\n border-radius: var(--mj-radius-md);\n font-size: var(--mj-text-lg);\n}\n\n.mj-page-header-text[_ngcontent-%COMP%] {\n min-width: 0;\n flex: 1 1 auto;\n}\n\n.mj-page-header-title-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n flex-wrap: wrap;\n min-width: 0;\n}\n\n.mj-page-header-title[_ngcontent-%COMP%] {\n margin: 0;\n font-size: var(--mj-text-xl);\n font-weight: var(--mj-font-semibold);\n color: var(--mj-text-primary);\n line-height: 1.25;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.mj-page-header-subtitle[_ngcontent-%COMP%] {\n margin: var(--mj-space-1) 0 0;\n font-size: var(--mj-text-sm);\n color: var(--mj-text-secondary);\n line-height: 1.4;\n}\n\n.mj-page-header-meta[_ngcontent-%COMP%], \n.mj-page-header-actions[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n flex-shrink: 0;\n}\n\n.mj-page-header-meta[_ngcontent-%COMP%]:empty, \n.mj-page-header-actions[_ngcontent-%COMP%]:empty {\n display: none;\n}\n\n@media (max-width: 768px) {\n .mj-page-header-row--primary[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: stretch;\n gap: var(--mj-space-3);\n }\n .mj-page-header-actions[_ngcontent-%COMP%] {\n justify-content: flex-end;\n }\n}"] });
80
+ }
81
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJPageHeaderComponent, [{
82
+ type: Component,
83
+ args: [{ selector: 'mj-page-header', standalone: true, template: `
84
+ <header class="mj-page-header">
85
+ <div class="mj-page-header-row mj-page-header-row--primary">
86
+ <div class="mj-page-header-titlebar">
87
+ @if (Icon) {
88
+ <div class="mj-page-header-icon" aria-hidden="true">
89
+ <i [class]="Icon"></i>
90
+ </div>
91
+ }
92
+ <div class="mj-page-header-text">
93
+ <div class="mj-page-header-title-row">
94
+ <h1 class="mj-page-header-title">{{ Title }}</h1>
95
+ <div class="mj-page-header-meta">
96
+ <ng-content select="[meta]"></ng-content>
97
+ </div>
98
+ </div>
99
+ @if (Subtitle) {
100
+ <p class="mj-page-header-subtitle">{{ Subtitle }}</p>
101
+ }
102
+ </div>
103
+ </div>
104
+ <div class="mj-page-header-actions">
105
+ <ng-content select="[actions]"></ng-content>
106
+ </div>
107
+ </div>
108
+ <div class="mj-page-header-row mj-page-header-row--toolbar">
109
+ <ng-content select="[toolbar]"></ng-content>
110
+ </div>
111
+ </header>
112
+ `, styles: [":host {\n display: block;\n /* External consumers using <mj-page-header> outside <mj-page-layout> get a\n 24px bottom margin so the header has breathing room from whatever follows.\n This preserves backward compatibility with the published v5.x contract. */\n margin-bottom: var(--mj-space-6);\n}\n\n/* When nested inside <mj-page-layout>, the spacing between header and body is\n owned by <mj-page-body>'s padding-top \u2014 so the header sits flush against the\n body. This lets pages using <mj-page-body [Padding]=\"false\"> (e.g. AI\n Analytics flush sidebar) render truly flush against the header. */\n:host-context(mj-page-layout) {\n margin-bottom: 0;\n}\n\n/* Slot-wrapper passthrough \u2014 any element a consumer projects with the `meta`,\n `actions`, or `toolbar` attribute (regardless of class name) becomes a\n transparent container so its children become direct flex children of the\n page-header row and inherit the row's gap. Eliminates the per-page\n `.{section}-header-meta { display: contents }` rules that pages had to\n declare in their own CSS files. */\n:host ::ng-deep [meta],\n:host ::ng-deep [actions],\n:host ::ng-deep [toolbar] {\n display: contents;\n}\n\n.mj-page-header {\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.mj-page-header-row {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n padding: 0 var(--mj-space-6);\n}\n\n.mj-page-header-row--primary {\n justify-content: space-between;\n gap: var(--mj-space-4);\n padding-top: var(--mj-space-4);\n padding-bottom: var(--mj-space-4);\n}\n\n.mj-page-header-row--toolbar {\n flex-wrap: wrap;\n padding-top: 0;\n padding-bottom: var(--mj-space-4);\n border-top: 1px solid var(--mj-border-subtle);\n margin-top: 0;\n padding-top: var(--mj-space-3);\n}\n\n.mj-page-header-row--toolbar:empty {\n display: none;\n border-top: 0;\n padding: 0;\n}\n\n.mj-page-header-titlebar {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.mj-page-header-icon {\n flex-shrink: 0;\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n color: var(--mj-brand-primary);\n border-radius: var(--mj-radius-md);\n font-size: var(--mj-text-lg);\n}\n\n.mj-page-header-text {\n min-width: 0;\n flex: 1 1 auto;\n}\n\n.mj-page-header-title-row {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n flex-wrap: wrap;\n min-width: 0;\n}\n\n.mj-page-header-title {\n margin: 0;\n font-size: var(--mj-text-xl);\n font-weight: var(--mj-font-semibold);\n color: var(--mj-text-primary);\n line-height: 1.25;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.mj-page-header-subtitle {\n margin: var(--mj-space-1) 0 0;\n font-size: var(--mj-text-sm);\n color: var(--mj-text-secondary);\n line-height: 1.4;\n}\n\n.mj-page-header-meta,\n.mj-page-header-actions {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n flex-shrink: 0;\n}\n\n.mj-page-header-meta:empty,\n.mj-page-header-actions:empty {\n display: none;\n}\n\n@media (max-width: 768px) {\n .mj-page-header-row--primary {\n flex-direction: column;\n align-items: stretch;\n gap: var(--mj-space-3);\n }\n .mj-page-header-actions {\n justify-content: flex-end;\n }\n}\n"] }]
113
+ }], null, { Title: [{
114
+ type: Input
115
+ }], Icon: [{
116
+ type: Input
117
+ }], Subtitle: [{
118
+ type: Input
119
+ }] }); })();
120
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJPageHeaderComponent, { className: "MJPageHeaderComponent", filePath: "lib/page-header/page-header.component.ts", lineNumber: 64 }); })();
121
+ //# sourceMappingURL=page-header.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-header.component.js","sourceRoot":"","sources":["../../../src/lib/page-header/page-header.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;;;;;IAqCrC,iCAAoD;IAClD,uBAAsB;IACxB,oBAAM;;;IADD,cAAc;IAAd,0BAAc;;;IAWjB,+BAAmC;IAAA,YAAc;IAAA,oBAAI;;;IAAlB,cAAc;IAAd,qCAAc;;AA/C/D;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAoCH,MAAM,OAAO,qBAAqB;IACvB,KAAK,GAAW,EAAE,CAAC;IACnB,IAAI,GAAkB,IAAI,CAAC;IAC3B,QAAQ,GAAkB,IAAI,CAAC;+GAH7B,qBAAqB;6DAArB,qBAAqB;;YA5B1B,AADF,AADF,oCAA+B,aAC+B,aACrB;YACnC,uFAAY;YAOR,AADF,AADF,iCAAiC,aACO,YACH;YAAA,YAAW;YAAA,oBAAK;YACjD,iCAAiC;YAC/B,kBAAyC;YAE7C,AADE,oBAAM,EACF;YACN,uFAAgB;YAIpB,AADE,oBAAM,EACF;YACN,kCAAoC;YAClC,sBAA4C;YAEhD,AADE,oBAAM,EACF;YACN,mCAA4D;YAC1D,sBAA4C;YAEhD,AADE,oBAAM,EACC;;YAxBH,eAIC;YAJD,mCAIC;YAGoC,eAAW;YAAX,+BAAW;YAK9C,eAEC;YAFD,wCAEC;;;iFAaA,qBAAqB;cAnCjC,SAAS;2BACE,gBAAgB,cACd,IAAI,YAEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BT;;kBAGA,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kFAHK,qBAAqB","sourcesContent":["import { Component, Input } from '@angular/core';\n\n/**\n * mj-page-header — Canonical page/dashboard header for MJ Explorer.\n *\n * Renders the standard icon · title · subtitle layout with three named projection\n * slots for additional content:\n * - `[meta]` — inline content immediately after the title text (result counts,\n * status badges) — wraps onto the title's row so it stays\n * horizontally adjacent to the title regardless of subtitle.\n * - `[actions]` — right-aligned action buttons or sub-navigation\n * - `[toolbar]` — optional secondary row beneath the title for dense controls\n * (filters, time-range pickers, breadcrumbs)\n *\n * Example:\n * ```html\n * <mj-page-header Title=\"Dashboard\" Icon=\"fa-solid fa-gauge\" Subtitle=\"Overview\">\n * <span meta class=\"badge\">{{ count }}</span>\n * <div actions>\n * <button mjButton variant=\"primary\">New</button>\n * </div>\n * <div toolbar>\n * <app-time-range-picker></app-time-range-picker>\n * <app-filter-bar></app-filter-bar>\n * </div>\n * </mj-page-header>\n * ```\n */\n@Component({\n selector: 'mj-page-header',\n standalone: true,\n styleUrls: ['./page-header.scss'],\n template: `\n <header class=\"mj-page-header\">\n <div class=\"mj-page-header-row mj-page-header-row--primary\">\n <div class=\"mj-page-header-titlebar\">\n @if (Icon) {\n <div class=\"mj-page-header-icon\" aria-hidden=\"true\">\n <i [class]=\"Icon\"></i>\n </div>\n }\n <div class=\"mj-page-header-text\">\n <div class=\"mj-page-header-title-row\">\n <h1 class=\"mj-page-header-title\">{{ Title }}</h1>\n <div class=\"mj-page-header-meta\">\n <ng-content select=\"[meta]\"></ng-content>\n </div>\n </div>\n @if (Subtitle) {\n <p class=\"mj-page-header-subtitle\">{{ Subtitle }}</p>\n }\n </div>\n </div>\n <div class=\"mj-page-header-actions\">\n <ng-content select=\"[actions]\"></ng-content>\n </div>\n </div>\n <div class=\"mj-page-header-row mj-page-header-row--toolbar\">\n <ng-content select=\"[toolbar]\"></ng-content>\n </div>\n </header>\n `\n})\nexport class MJPageHeaderComponent {\n @Input() Title: string = '';\n @Input() Icon: string | null = null;\n @Input() Subtitle: string | null = null;\n}\n"]}