@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.
- package/dist/lib/filter-chip/filter-chip.component.d.ts +31 -0
- package/dist/lib/filter-chip/filter-chip.component.d.ts.map +1 -0
- package/dist/lib/filter-chip/filter-chip.component.js +94 -0
- package/dist/lib/filter-chip/filter-chip.component.js.map +1 -0
- package/dist/lib/filter-field/filter-field.component.d.ts +22 -0
- package/dist/lib/filter-field/filter-field.component.d.ts.map +1 -0
- package/dist/lib/filter-field/filter-field.component.js +60 -0
- package/dist/lib/filter-field/filter-field.component.js.map +1 -0
- package/dist/lib/filter-panel/filter-panel.component.d.ts +69 -0
- package/dist/lib/filter-panel/filter-panel.component.d.ts.map +1 -0
- package/dist/lib/filter-panel/filter-panel.component.js +203 -0
- package/dist/lib/filter-panel/filter-panel.component.js.map +1 -0
- package/dist/lib/filter-popover/filter-popover.component.d.ts +38 -0
- package/dist/lib/filter-popover/filter-popover.component.d.ts.map +1 -0
- package/dist/lib/filter-popover/filter-popover.component.js +170 -0
- package/dist/lib/filter-popover/filter-popover.component.js.map +1 -0
- package/dist/lib/page-body/page-body.component.d.ts +46 -0
- package/dist/lib/page-body/page-body.component.d.ts.map +1 -0
- package/dist/lib/page-body/page-body.component.js +72 -0
- package/dist/lib/page-body/page-body.component.js.map +1 -0
- package/dist/lib/page-header/page-header.component.d.ts +35 -0
- package/dist/lib/page-header/page-header.component.d.ts.map +1 -0
- package/dist/lib/page-header/page-header.component.js +121 -0
- package/dist/lib/page-header/page-header.component.js.map +1 -0
- package/dist/lib/page-header/page-header.scss +139 -0
- package/dist/lib/page-layout/page-layout.component.d.ts +20 -0
- package/dist/lib/page-layout/page-layout.component.d.ts.map +1 -0
- package/dist/lib/page-layout/page-layout.component.js +30 -0
- package/dist/lib/page-layout/page-layout.component.js.map +1 -0
- package/dist/lib/page-search/page-search.component.d.ts +29 -0
- package/dist/lib/page-search/page-search.component.d.ts.map +1 -0
- package/dist/lib/page-search/page-search.component.js +69 -0
- package/dist/lib/page-search/page-search.component.js.map +1 -0
- package/dist/lib/refresh-button/refresh-button.component.d.ts +46 -0
- package/dist/lib/refresh-button/refresh-button.component.d.ts.map +1 -0
- package/dist/lib/refresh-button/refresh-button.component.js +106 -0
- package/dist/lib/refresh-button/refresh-button.component.js.map +1 -0
- package/dist/lib/stat-badge/stat-badge.component.d.ts +54 -0
- package/dist/lib/stat-badge/stat-badge.component.d.ts.map +1 -0
- package/dist/lib/stat-badge/stat-badge.component.js +162 -0
- package/dist/lib/stat-badge/stat-badge.component.js.map +1 -0
- package/dist/lib/tab-nav/tab-nav.component.d.ts +41 -0
- package/dist/lib/tab-nav/tab-nav.component.d.ts.map +1 -0
- package/dist/lib/tab-nav/tab-nav.component.js +111 -0
- package/dist/lib/tab-nav/tab-nav.component.js.map +1 -0
- package/dist/lib/view-toggle/view-toggle.component.d.ts +42 -0
- package/dist/lib/view-toggle/view-toggle.component.d.ts.map +1 -0
- package/dist/lib/view-toggle/view-toggle.component.js +104 -0
- package/dist/lib/view-toggle/view-toggle.component.js.map +1 -0
- package/dist/public-api.d.ts +12 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +12 -0
- package/dist/public-api.js.map +1 -1
- 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"]}
|