@memberjunction/ng-ui-components 5.35.0 → 5.37.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 (34) hide show
  1. package/dist/lib/button/button.scss +7 -3
  2. package/dist/lib/filter-popover/filter-popover.component.d.ts.map +1 -1
  3. package/dist/lib/filter-popover/filter-popover.component.js +3 -3
  4. package/dist/lib/filter-popover/filter-popover.component.js.map +1 -1
  5. package/dist/lib/left-nav/left-nav-content.component.d.ts +53 -0
  6. package/dist/lib/left-nav/left-nav-content.component.d.ts.map +1 -0
  7. package/dist/lib/left-nav/left-nav-content.component.js +121 -0
  8. package/dist/lib/left-nav/left-nav-content.component.js.map +1 -0
  9. package/dist/lib/left-nav/left-nav.component.d.ts +137 -0
  10. package/dist/lib/left-nav/left-nav.component.d.ts.map +1 -0
  11. package/dist/lib/left-nav/left-nav.component.js +399 -0
  12. package/dist/lib/left-nav/left-nav.component.js.map +1 -0
  13. package/dist/lib/page-body/page-body.component.d.ts +12 -2
  14. package/dist/lib/page-body/page-body.component.d.ts.map +1 -1
  15. package/dist/lib/page-body/page-body.component.js +24 -7
  16. package/dist/lib/page-body/page-body.component.js.map +1 -1
  17. package/dist/lib/page-body-interior/page-body-interior.component.d.ts +62 -0
  18. package/dist/lib/page-body-interior/page-body-interior.component.d.ts.map +1 -0
  19. package/dist/lib/page-body-interior/page-body-interior.component.js +95 -0
  20. package/dist/lib/page-body-interior/page-body-interior.component.js.map +1 -0
  21. package/dist/lib/page-header/page-header.component.d.ts +12 -4
  22. package/dist/lib/page-header/page-header.component.d.ts.map +1 -1
  23. package/dist/lib/page-header/page-header.component.js +33 -28
  24. package/dist/lib/page-header/page-header.component.js.map +1 -1
  25. package/dist/lib/page-header/page-header.scss +30 -9
  26. package/dist/lib/page-header-interior/page-header-interior.component.d.ts +96 -0
  27. package/dist/lib/page-header-interior/page-header-interior.component.d.ts.map +1 -0
  28. package/dist/lib/page-header-interior/page-header-interior.component.js +176 -0
  29. package/dist/lib/page-header-interior/page-header-interior.component.js.map +1 -0
  30. package/dist/public-api.d.ts +4 -0
  31. package/dist/public-api.d.ts.map +1 -1
  32. package/dist/public-api.js +4 -0
  33. package/dist/public-api.js.map +1 -1
  34. package/package.json +1 -1
@@ -0,0 +1,62 @@
1
+ import * as i0 from "@angular/core";
2
+ /**
3
+ * `<mj-page-body-interior>` — Body-region sibling of `<mj-page-header-interior>`.
4
+ *
5
+ * Paired primitive for sub-pages that render inside a left-nav workspace shell.
6
+ * Where `<mj-page-body>` is for top-level pages (sits inside `<mj-page-layout>`),
7
+ * this component is for sub-pages whose host element already has the flex-column
8
+ * layout (typically via `_admin-patterns.css`'s `:host` rule or an equivalent).
9
+ *
10
+ * Owns:
11
+ * - `flex: 1` so it fills the space below `<mj-page-header-interior>`.
12
+ * - `overflow-y: auto` so the body scrolls (the host typically has `overflow: hidden`).
13
+ * - Responsive padding: 16px on mobile, 24px at 768px+, 32px at 1024px+.
14
+ * Mirrors the bespoke `.scrollable-content` rules in
15
+ * `packages/Angular/Explorer/explorer-settings/src/lib/shared/styles/_admin-patterns.css`.
16
+ *
17
+ * Background is fixed to `--mj-bg-page` to match `<mj-left-nav-content>`'s
18
+ * background (the typical parent in shell sub-page contexts). The interior
19
+ * chrome's own surface + shadow provides the visual delineation between chrome
20
+ * and body — no second background tone needed.
21
+ *
22
+ * Escape hatches:
23
+ * - `[Padding]="false"` — remove the gutter (e.g. when inner content owns padding).
24
+ * - `[Flex]="true"` + `[Direction]="'row'"` — switch the body to a flex container
25
+ * so children with `flex: 1` can grow (e.g. sidebar + content panes).
26
+ *
27
+ * Example:
28
+ * ```html
29
+ * <mj-page-header-interior Title="Users" Subtitle="...">
30
+ * <div toolbar>...</div>
31
+ * <div actions>...</div>
32
+ * </mj-page-header-interior>
33
+ * <mj-page-body-interior>
34
+ * <!-- content; scroll, padding, background all handled -->
35
+ * </mj-page-body-interior>
36
+ * ```
37
+ *
38
+ * See Section 10 of `plans/explorer-chrome-conventions.md` for the full
39
+ * "interior chrome" contract.
40
+ */
41
+ export declare class MJPageBodyInteriorComponent {
42
+ /** When `false`, removes the responsive gutter. Defaults to `true`. */
43
+ Padding: boolean;
44
+ /**
45
+ * When `true`, switches the body to a flex container so children with
46
+ * `flex: 1` can fill remaining space (e.g. sidebar + content). Defaults to
47
+ * `false` (block flow with vertical scroll).
48
+ */
49
+ Flex: boolean;
50
+ /**
51
+ * When `Flex=true`, controls flex direction. `'column'` (default) stacks
52
+ * children vertically; `'row'` for left-rail + content sub-page layouts.
53
+ * Ignored when `Flex=false`.
54
+ */
55
+ Direction: 'row' | 'column';
56
+ get NoPaddingClass(): boolean;
57
+ get FlexClass(): boolean;
58
+ get RowClass(): boolean;
59
+ static ɵfac: i0.ɵɵFactoryDeclaration<MJPageBodyInteriorComponent, never>;
60
+ static ɵcmp: i0.ɵɵComponentDeclaration<MJPageBodyInteriorComponent, "mj-page-body-interior", never, { "Padding": { "alias": "Padding"; "required": false; }; "Flex": { "alias": "Flex"; "required": false; }; "Direction": { "alias": "Direction"; "required": false; }; }, {}, never, ["*"], true, never>;
61
+ }
62
+ //# sourceMappingURL=page-body-interior.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-body-interior.component.d.ts","sourceRoot":"","sources":["../../../src/lib/page-body-interior/page-body-interior.component.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,qBA6Ca,2BAA2B;IACtC,uEAAuE;IAC9D,OAAO,EAAE,OAAO,CAAQ;IAEjC;;;;OAIG;IACM,IAAI,EAAE,OAAO,CAAS;IAE/B;;;;OAIG;IACM,SAAS,EAAE,KAAK,GAAG,QAAQ,CAAY;IAEhD,IACI,cAAc,IAAI,OAAO,CAE5B;IAED,IACI,SAAS,IAAI,OAAO,CAEvB;IAED,IACI,QAAQ,IAAI,OAAO,CAEtB;yCA/BU,2BAA2B;2CAA3B,2BAA2B;CAgCvC"}
@@ -0,0 +1,95 @@
1
+ import { Component, HostBinding, Input } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ const _c0 = ["*"];
4
+ /**
5
+ * `<mj-page-body-interior>` — Body-region sibling of `<mj-page-header-interior>`.
6
+ *
7
+ * Paired primitive for sub-pages that render inside a left-nav workspace shell.
8
+ * Where `<mj-page-body>` is for top-level pages (sits inside `<mj-page-layout>`),
9
+ * this component is for sub-pages whose host element already has the flex-column
10
+ * layout (typically via `_admin-patterns.css`'s `:host` rule or an equivalent).
11
+ *
12
+ * Owns:
13
+ * - `flex: 1` so it fills the space below `<mj-page-header-interior>`.
14
+ * - `overflow-y: auto` so the body scrolls (the host typically has `overflow: hidden`).
15
+ * - Responsive padding: 16px on mobile, 24px at 768px+, 32px at 1024px+.
16
+ * Mirrors the bespoke `.scrollable-content` rules in
17
+ * `packages/Angular/Explorer/explorer-settings/src/lib/shared/styles/_admin-patterns.css`.
18
+ *
19
+ * Background is fixed to `--mj-bg-page` to match `<mj-left-nav-content>`'s
20
+ * background (the typical parent in shell sub-page contexts). The interior
21
+ * chrome's own surface + shadow provides the visual delineation between chrome
22
+ * and body — no second background tone needed.
23
+ *
24
+ * Escape hatches:
25
+ * - `[Padding]="false"` — remove the gutter (e.g. when inner content owns padding).
26
+ * - `[Flex]="true"` + `[Direction]="'row'"` — switch the body to a flex container
27
+ * so children with `flex: 1` can grow (e.g. sidebar + content panes).
28
+ *
29
+ * Example:
30
+ * ```html
31
+ * <mj-page-header-interior Title="Users" Subtitle="...">
32
+ * <div toolbar>...</div>
33
+ * <div actions>...</div>
34
+ * </mj-page-header-interior>
35
+ * <mj-page-body-interior>
36
+ * <!-- content; scroll, padding, background all handled -->
37
+ * </mj-page-body-interior>
38
+ * ```
39
+ *
40
+ * See Section 10 of `plans/explorer-chrome-conventions.md` for the full
41
+ * "interior chrome" contract.
42
+ */
43
+ export class MJPageBodyInteriorComponent {
44
+ /** When `false`, removes the responsive gutter. Defaults to `true`. */
45
+ Padding = true;
46
+ /**
47
+ * When `true`, switches the body to a flex container so children with
48
+ * `flex: 1` can fill remaining space (e.g. sidebar + content). Defaults to
49
+ * `false` (block flow with vertical scroll).
50
+ */
51
+ Flex = false;
52
+ /**
53
+ * When `Flex=true`, controls flex direction. `'column'` (default) stacks
54
+ * children vertically; `'row'` for left-rail + content sub-page layouts.
55
+ * Ignored when `Flex=false`.
56
+ */
57
+ Direction = 'column';
58
+ get NoPaddingClass() {
59
+ return !this.Padding;
60
+ }
61
+ get FlexClass() {
62
+ return this.Flex;
63
+ }
64
+ get RowClass() {
65
+ return this.Flex && this.Direction === 'row';
66
+ }
67
+ static ɵfac = function MJPageBodyInteriorComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MJPageBodyInteriorComponent)(); };
68
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJPageBodyInteriorComponent, selectors: [["mj-page-body-interior"]], hostVars: 6, hostBindings: function MJPageBodyInteriorComponent_HostBindings(rf, ctx) { if (rf & 2) {
69
+ i0.ɵɵclassProp("mj-page-body-interior--no-padding", ctx.NoPaddingClass)("mj-page-body-interior--flex", ctx.FlexClass)("mj-page-body-interior--row", ctx.RowClass);
70
+ } }, inputs: { Padding: "Padding", Flex: "Flex", Direction: "Direction" }, ngContentSelectors: _c0, decls: 1, vars: 0, template: function MJPageBodyInteriorComponent_Template(rf, ctx) { if (rf & 1) {
71
+ i0.ɵɵprojectionDef();
72
+ i0.ɵɵprojection(0);
73
+ } }, styles: ["[_nghost-%COMP%] {\n display: block;\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n overflow-x: hidden;\n -webkit-overflow-scrolling: touch;\n \n\n\n\n\n padding: var(--mj-space-2) var(--mj-space-4) var(--mj-space-4);\n background: var(--mj-bg-page);\n }\n\n @media (min-width: 768px) {\n [_nghost-%COMP%] { padding: var(--mj-space-2) var(--mj-space-6) var(--mj-space-6); }\n }\n\n @media (min-width: 1024px) {\n [_nghost-%COMP%] { padding: var(--mj-space-2) var(--mj-space-8) var(--mj-space-8); }\n }\n\n \n\n .mj-page-body-interior--no-padding[_nghost-%COMP%] {\n padding: 0;\n }\n\n \n\n .mj-page-body-interior--flex[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .mj-page-body-interior--flex.mj-page-body-interior--row[_nghost-%COMP%] {\n flex-direction: row;\n }"] });
74
+ }
75
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJPageBodyInteriorComponent, [{
76
+ type: Component,
77
+ args: [{ selector: 'mj-page-body-interior', standalone: true, template: `<ng-content></ng-content>`, styles: ["\n :host {\n display: block;\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n overflow-x: hidden;\n -webkit-overflow-scrolling: touch;\n /* Top padding is intentionally tighter than sides/bottom \u2014 the body is\n almost always preceded by an <mj-page-header-interior> whose own\n bottom margin provides the visual breathing room above the body\n content. Sides + bottom scale responsively for comfortable gutters. */\n padding: var(--mj-space-2) var(--mj-space-4) var(--mj-space-4);\n background: var(--mj-bg-page);\n }\n\n @media (min-width: 768px) {\n :host { padding: var(--mj-space-2) var(--mj-space-6) var(--mj-space-6); }\n }\n\n @media (min-width: 1024px) {\n :host { padding: var(--mj-space-2) var(--mj-space-8) var(--mj-space-8); }\n }\n\n /* No-padding escape hatch \u2014 for sub-pages whose inner content owns the gutter. */\n :host(.mj-page-body-interior--no-padding) {\n padding: 0;\n }\n\n /* Flex container mode \u2014 for sub-pages with sidebar + content layouts. */\n :host(.mj-page-body-interior--flex) {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n :host(.mj-page-body-interior--flex.mj-page-body-interior--row) {\n flex-direction: row;\n }\n "] }]
78
+ }], null, { Padding: [{
79
+ type: Input
80
+ }], Flex: [{
81
+ type: Input
82
+ }], Direction: [{
83
+ type: Input
84
+ }], NoPaddingClass: [{
85
+ type: HostBinding,
86
+ args: ['class.mj-page-body-interior--no-padding']
87
+ }], FlexClass: [{
88
+ type: HostBinding,
89
+ args: ['class.mj-page-body-interior--flex']
90
+ }], RowClass: [{
91
+ type: HostBinding,
92
+ args: ['class.mj-page-body-interior--row']
93
+ }] }); })();
94
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJPageBodyInteriorComponent, { className: "MJPageBodyInteriorComponent", filePath: "lib/page-body-interior/page-body-interior.component.ts", lineNumber: 87 }); })();
95
+ //# sourceMappingURL=page-body-interior.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-body-interior.component.js","sourceRoot":"","sources":["../../../src/lib/page-body-interior/page-body-interior.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;;;AAE9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AA8CH,MAAM,OAAO,2BAA2B;IACtC,uEAAuE;IAC9D,OAAO,GAAY,IAAI,CAAC;IAEjC;;;;OAIG;IACM,IAAI,GAAY,KAAK,CAAC;IAE/B;;;;OAIG;IACM,SAAS,GAAqB,QAAQ,CAAC;IAEhD,IACI,cAAc;QAChB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;IACvB,CAAC;IAED,IACI,SAAS;QACX,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED,IACI,QAAQ;QACV,OAAO,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC;IAC/C,CAAC;qHA/BU,2BAA2B;6DAA3B,2BAA2B;YAA3B,uEAA2B,8CAAA,4CAAA;;;YA1C3B,kBAAyB;;;iFA0CzB,2BAA2B;cA7CvC,SAAS;2BACE,uBAAuB,cACrB,IAAI,YACN,2BAA2B;;kBA4CpC,KAAK;;kBAOL,KAAK;;kBAOL,KAAK;;kBAEL,WAAW;mBAAC,yCAAyC;;kBAKrD,WAAW;mBAAC,mCAAmC;;kBAK/C,WAAW;mBAAC,kCAAkC;;kFA5BpC,2BAA2B","sourcesContent":["import { Component, HostBinding, Input } from '@angular/core';\n\n/**\n * `<mj-page-body-interior>` — Body-region sibling of `<mj-page-header-interior>`.\n *\n * Paired primitive for sub-pages that render inside a left-nav workspace shell.\n * Where `<mj-page-body>` is for top-level pages (sits inside `<mj-page-layout>`),\n * this component is for sub-pages whose host element already has the flex-column\n * layout (typically via `_admin-patterns.css`'s `:host` rule or an equivalent).\n *\n * Owns:\n * - `flex: 1` so it fills the space below `<mj-page-header-interior>`.\n * - `overflow-y: auto` so the body scrolls (the host typically has `overflow: hidden`).\n * - Responsive padding: 16px on mobile, 24px at 768px+, 32px at 1024px+.\n * Mirrors the bespoke `.scrollable-content` rules in\n * `packages/Angular/Explorer/explorer-settings/src/lib/shared/styles/_admin-patterns.css`.\n *\n * Background is fixed to `--mj-bg-page` to match `<mj-left-nav-content>`'s\n * background (the typical parent in shell sub-page contexts). The interior\n * chrome's own surface + shadow provides the visual delineation between chrome\n * and body — no second background tone needed.\n *\n * Escape hatches:\n * - `[Padding]=\"false\"` — remove the gutter (e.g. when inner content owns padding).\n * - `[Flex]=\"true\"` + `[Direction]=\"'row'\"` — switch the body to a flex container\n * so children with `flex: 1` can grow (e.g. sidebar + content panes).\n *\n * Example:\n * ```html\n * <mj-page-header-interior Title=\"Users\" Subtitle=\"...\">\n * <div toolbar>...</div>\n * <div actions>...</div>\n * </mj-page-header-interior>\n * <mj-page-body-interior>\n * <!-- content; scroll, padding, background all handled -->\n * </mj-page-body-interior>\n * ```\n *\n * See Section 10 of `plans/explorer-chrome-conventions.md` for the full\n * \"interior chrome\" contract.\n */\n@Component({\n selector: 'mj-page-body-interior',\n standalone: true,\n template: `<ng-content></ng-content>`,\n styles: [`\n :host {\n display: block;\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n overflow-x: hidden;\n -webkit-overflow-scrolling: touch;\n /* Top padding is intentionally tighter than sides/bottom — the body is\n almost always preceded by an <mj-page-header-interior> whose own\n bottom margin provides the visual breathing room above the body\n content. Sides + bottom scale responsively for comfortable gutters. */\n padding: var(--mj-space-2) var(--mj-space-4) var(--mj-space-4);\n background: var(--mj-bg-page);\n }\n\n @media (min-width: 768px) {\n :host { padding: var(--mj-space-2) var(--mj-space-6) var(--mj-space-6); }\n }\n\n @media (min-width: 1024px) {\n :host { padding: var(--mj-space-2) var(--mj-space-8) var(--mj-space-8); }\n }\n\n /* No-padding escape hatch — for sub-pages whose inner content owns the gutter. */\n :host(.mj-page-body-interior--no-padding) {\n padding: 0;\n }\n\n /* Flex container mode — for sub-pages with sidebar + content layouts. */\n :host(.mj-page-body-interior--flex) {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n :host(.mj-page-body-interior--flex.mj-page-body-interior--row) {\n flex-direction: row;\n }\n `]\n})\nexport class MJPageBodyInteriorComponent {\n /** When `false`, removes the responsive gutter. Defaults to `true`. */\n @Input() Padding: boolean = true;\n\n /**\n * When `true`, switches the body to a flex container so children with\n * `flex: 1` can fill remaining space (e.g. sidebar + content). Defaults to\n * `false` (block flow with vertical scroll).\n */\n @Input() Flex: boolean = false;\n\n /**\n * When `Flex=true`, controls flex direction. `'column'` (default) stacks\n * children vertically; `'row'` for left-rail + content sub-page layouts.\n * Ignored when `Flex=false`.\n */\n @Input() Direction: 'row' | 'column' = 'column';\n\n @HostBinding('class.mj-page-body-interior--no-padding')\n get NoPaddingClass(): boolean {\n return !this.Padding;\n }\n\n @HostBinding('class.mj-page-body-interior--flex')\n get FlexClass(): boolean {\n return this.Flex;\n }\n\n @HostBinding('class.mj-page-body-interior--row')\n get RowClass(): boolean {\n return this.Flex && this.Direction === 'row';\n }\n}\n"]}
@@ -4,10 +4,14 @@ import * as i0 from "@angular/core";
4
4
  *
5
5
  * Renders the standard icon · title · subtitle layout with three named projection
6
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
7
+ * - `[meta]` — supporting info that lives **below the subtitle** in the
8
+ * identity column (result counts, status badges, stat-pills).
9
+ * Reads as data attached to the identity, not as a peer of
10
+ * the title — keeps the title the dominant visual anchor.
11
+ * Wraps when there are many badges.
12
+ * - `[actions]` — right-aligned action buttons or sub-navigation, top-aligned
13
+ * with the title row so they stay anchored regardless of how
14
+ * tall the identity column grows.
11
15
  * - `[toolbar]` — optional secondary row beneath the title for dense controls
12
16
  * (filters, time-range pickers, breadcrumbs)
13
17
  *
@@ -24,6 +28,10 @@ import * as i0 from "@angular/core";
24
28
  * </div>
25
29
  * </mj-page-header>
26
30
  * ```
31
+ *
32
+ * For the body-level interior chrome variant used by left-nav sub-pages, use
33
+ * `<mj-page-header-interior>` instead — same slot conventions, different
34
+ * visual shape (single-row card, no titlebar).
27
35
  */
28
36
  export declare class MJPageHeaderComponent {
29
37
  Title: string;
@@ -1 +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"}
1
+ {"version":3,"file":"page-header.component.d.ts","sourceRoot":"","sources":["../../../src/lib/page-header/page-header.component.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBAiCa,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"}
@@ -11,8 +11,8 @@ function MJPageHeaderComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
11
11
  i0.ɵɵadvance();
12
12
  i0.ɵɵclassMap(ctx_r0.Icon);
13
13
  } }
14
- function MJPageHeaderComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
15
- i0.ɵɵdomElementStart(0, "p", 8);
14
+ function MJPageHeaderComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
15
+ i0.ɵɵdomElementStart(0, "p", 6);
16
16
  i0.ɵɵtext(1);
17
17
  i0.ɵɵdomElementEnd();
18
18
  } if (rf & 2) {
@@ -25,10 +25,14 @@ function MJPageHeaderComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
25
25
  *
26
26
  * Renders the standard icon · title · subtitle layout with three named projection
27
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
28
+ * - `[meta]` — supporting info that lives **below the subtitle** in the
29
+ * identity column (result counts, status badges, stat-pills).
30
+ * Reads as data attached to the identity, not as a peer of
31
+ * the title — keeps the title the dominant visual anchor.
32
+ * Wraps when there are many badges.
33
+ * - `[actions]` — right-aligned action buttons or sub-navigation, top-aligned
34
+ * with the title row so they stay anchored regardless of how
35
+ * tall the identity column grows.
32
36
  * - `[toolbar]` — optional secondary row beneath the title for dense controls
33
37
  * (filters, time-range pickers, breadcrumbs)
34
38
  *
@@ -45,38 +49,41 @@ function MJPageHeaderComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
45
49
  * </div>
46
50
  * </mj-page-header>
47
51
  * ```
52
+ *
53
+ * For the body-level interior chrome variant used by left-nav sub-pages, use
54
+ * `<mj-page-header-interior>` instead — same slot conventions, different
55
+ * visual shape (single-row card, no titlebar).
48
56
  */
49
57
  export class MJPageHeaderComponent {
50
58
  Title = '';
51
59
  Icon = null;
52
60
  Subtitle = null;
53
61
  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) {
62
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJPageHeaderComponent, selectors: [["mj-page-header"]], inputs: { Title: "Title", Icon: "Icon", Subtitle: "Subtitle" }, ngContentSelectors: _c1, decls: 14, 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"], [1, "mj-page-header-subtitle"], [1, "mj-page-header-meta"], [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
63
  i0.ɵɵprojectionDef(_c0);
56
64
  i0.ɵɵdomElementStart(0, "header", 0)(1, "div", 1)(2, "div", 2);
57
65
  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);
66
+ i0.ɵɵdomElementStart(4, "div", 4)(5, "h1", 5);
67
+ i0.ɵɵtext(6);
60
68
  i0.ɵɵdomElementEnd();
69
+ i0.ɵɵconditionalCreate(7, MJPageHeaderComponent_Conditional_7_Template, 2, 1, "p", 6);
61
70
  i0.ɵɵdomElementStart(8, "div", 7);
62
71
  i0.ɵɵprojection(9);
72
+ i0.ɵɵdomElementEnd()()();
73
+ i0.ɵɵdomElementStart(10, "div", 8);
74
+ i0.ɵɵprojection(11, 1);
63
75
  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);
76
+ i0.ɵɵdomElementStart(12, "div", 9);
77
+ i0.ɵɵprojection(13, 2);
71
78
  i0.ɵɵdomElementEnd()();
72
79
  } if (rf & 2) {
73
80
  i0.ɵɵadvance(3);
74
81
  i0.ɵɵconditional(ctx.Icon ? 3 : -1);
75
- i0.ɵɵadvance(4);
76
- i0.ɵɵtextInterpolate(ctx.Title);
77
82
  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}"] });
83
+ i0.ɵɵtextInterpolate(ctx.Title);
84
+ i0.ɵɵadvance();
85
+ i0.ɵɵconditional(ctx.Subtitle ? 7 : -1);
86
+ } }, 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 \n\n\n\n\n\n\n box-shadow: var(--mj-shadow-md);\n position: relative;\n z-index: 1;\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\n\n\n\n\n\n\n.mj-page-header-row--primary[_ngcontent-%COMP%] {\n justify-content: space-between;\n align-items: center;\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\n\n.mj-page-header-text[_ngcontent-%COMP%] {\n min-width: 0;\n flex: 1 1 auto;\n display: flex;\n flex-direction: column;\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\n\n\n\n\n.mj-page-header-meta[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--mj-space-2);\n margin-top: var(--mj-space-2);\n}\n\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
87
  }
81
88
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJPageHeaderComponent, [{
82
89
  type: Component,
@@ -90,15 +97,13 @@ export class MJPageHeaderComponent {
90
97
  </div>
91
98
  }
92
99
  <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>
100
+ <h1 class="mj-page-header-title">{{ Title }}</h1>
99
101
  @if (Subtitle) {
100
102
  <p class="mj-page-header-subtitle">{{ Subtitle }}</p>
101
103
  }
104
+ <div class="mj-page-header-meta">
105
+ <ng-content select="[meta]"></ng-content>
106
+ </div>
102
107
  </div>
103
108
  </div>
104
109
  <div class="mj-page-header-actions">
@@ -109,7 +114,7 @@ export class MJPageHeaderComponent {
109
114
  <ng-content select="[toolbar]"></ng-content>
110
115
  </div>
111
116
  </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"] }]
117
+ `, 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 /* Drop shadow for visual separation from the body below.\n `position: relative; z-index: 1` lifts the header into its own stacking\n context above <mj-page-body> (which has `position: relative` and would\n otherwise paint over the downward shadow as a later flex sibling).\n --mj-shadow-md is used instead of --mj-shadow-sm because the latter is\n rgba(0,0,0,0.05) in light mode \u2014 too faint to read as separation. */\n box-shadow: var(--mj-shadow-md);\n position: relative;\n z-index: 1;\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/* Primary row centers the actions cluster vertically against the identity\n column. The identity column stacks title \u2192 subtitle \u2192 (optional meta);\n centering keeps the actions visually balanced against the whole block\n instead of pinning them to the title baseline. When meta badges are\n present the actions drift down a touch \u2014 acceptable trade for the more\n common no-meta case where flex-start looks top-heavy. */\n.mj-page-header-row--primary {\n justify-content: space-between;\n align-items: center;\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/* Identity column \u2014 title, subtitle, meta stacked vertically. */\n.mj-page-header-text {\n min-width: 0;\n flex: 1 1 auto;\n display: flex;\n flex-direction: column;\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/* Meta lives in the identity column UNDER the subtitle \u2014 supporting info\n for the identity, not a peer to the title. Wraps when there are many\n badges. Matches the mj-page-header-interior pattern so both chrome\n surfaces have one mental model. */\n.mj-page-header-meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--mj-space-2);\n margin-top: var(--mj-space-2);\n}\n\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
118
  }], null, { Title: [{
114
119
  type: Input
115
120
  }], Icon: [{
@@ -117,5 +122,5 @@ export class MJPageHeaderComponent {
117
122
  }], Subtitle: [{
118
123
  type: Input
119
124
  }] }); })();
120
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJPageHeaderComponent, { className: "MJPageHeaderComponent", filePath: "lib/page-header/page-header.component.ts", lineNumber: 64 }); })();
125
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJPageHeaderComponent, { className: "MJPageHeaderComponent", filePath: "lib/page-header/page-header.component.ts", lineNumber: 70 }); })();
121
126
  //# sourceMappingURL=page-header.component.js.map
@@ -1 +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"]}
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;;;;;IA6CrC,iCAAoD;IAClD,uBAAsB;IACxB,oBAAM;;;IADD,cAAc;IAAd,0BAAc;;;IAMjB,+BAAmC;IAAA,YAAc;IAAA,oBAAI;;;IAAlB,cAAc;IAAd,qCAAc;;AAlD/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAkCH,MAAM,OAAO,qBAAqB;IACvB,KAAK,GAAW,EAAE,CAAC;IACnB,IAAI,GAAkB,IAAI,CAAC;IAC3B,QAAQ,GAAkB,IAAI,CAAC;+GAH7B,qBAAqB;6DAArB,qBAAqB;;YA1B1B,AADF,AADF,oCAA+B,aAC+B,aACrB;YACnC,uFAAY;YAMV,AADF,iCAAiC,YACE;YAAA,YAAW;YAAA,oBAAK;YACjD,qFAAgB;YAGhB,iCAAiC;YAC/B,kBAAyC;YAG/C,AADE,AADE,oBAAM,EACF,EACF;YACN,kCAAoC;YAClC,sBAA4C;YAEhD,AADE,oBAAM,EACF;YACN,kCAA4D;YAC1D,sBAA4C;YAEhD,AADE,oBAAM,EACC;;YAtBH,eAIC;YAJD,mCAIC;YAEkC,eAAW;YAAX,+BAAW;YAC5C,cAEC;YAFD,uCAEC;;;iFAgBA,qBAAqB;cAjCjC,SAAS;2BACE,gBAAgB,cACd,IAAI,YAEN;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BT;;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]` — supporting info that lives **below the subtitle** in the\n * identity column (result counts, status badges, stat-pills).\n * Reads as data attached to the identity, not as a peer of\n * the title — keeps the title the dominant visual anchor.\n * Wraps when there are many badges.\n * - `[actions]` — right-aligned action buttons or sub-navigation, top-aligned\n * with the title row so they stay anchored regardless of how\n * tall the identity column grows.\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 * For the body-level interior chrome variant used by left-nav sub-pages, use\n * `<mj-page-header-interior>` instead — same slot conventions, different\n * visual shape (single-row card, no titlebar).\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 <h1 class=\"mj-page-header-title\">{{ Title }}</h1>\n @if (Subtitle) {\n <p class=\"mj-page-header-subtitle\">{{ Subtitle }}</p>\n }\n <div class=\"mj-page-header-meta\">\n <ng-content select=\"[meta]\"></ng-content>\n </div>\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"]}
@@ -30,7 +30,15 @@
30
30
  display: flex;
31
31
  flex-direction: column;
32
32
  background: var(--mj-bg-surface);
33
- box-shadow: var(--mj-shadow-sm);
33
+ /* Drop shadow for visual separation from the body below.
34
+ `position: relative; z-index: 1` lifts the header into its own stacking
35
+ context above <mj-page-body> (which has `position: relative` and would
36
+ otherwise paint over the downward shadow as a later flex sibling).
37
+ --mj-shadow-md is used instead of --mj-shadow-sm because the latter is
38
+ rgba(0,0,0,0.05) in light mode — too faint to read as separation. */
39
+ box-shadow: var(--mj-shadow-md);
40
+ position: relative;
41
+ z-index: 1;
34
42
  }
35
43
 
36
44
  .mj-page-header-row {
@@ -40,8 +48,15 @@
40
48
  padding: 0 var(--mj-space-6);
41
49
  }
42
50
 
51
+ /* Primary row centers the actions cluster vertically against the identity
52
+ column. The identity column stacks title → subtitle → (optional meta);
53
+ centering keeps the actions visually balanced against the whole block
54
+ instead of pinning them to the title baseline. When meta badges are
55
+ present the actions drift down a touch — acceptable trade for the more
56
+ common no-meta case where flex-start looks top-heavy. */
43
57
  .mj-page-header-row--primary {
44
58
  justify-content: space-between;
59
+ align-items: center;
45
60
  gap: var(--mj-space-4);
46
61
  padding-top: var(--mj-space-4);
47
62
  padding-bottom: var(--mj-space-4);
@@ -83,17 +98,12 @@
83
98
  font-size: var(--mj-text-lg);
84
99
  }
85
100
 
101
+ /* Identity column — title, subtitle, meta stacked vertically. */
86
102
  .mj-page-header-text {
87
103
  min-width: 0;
88
104
  flex: 1 1 auto;
89
- }
90
-
91
- .mj-page-header-title-row {
92
105
  display: flex;
93
- align-items: center;
94
- gap: var(--mj-space-3);
95
- flex-wrap: wrap;
96
- min-width: 0;
106
+ flex-direction: column;
97
107
  }
98
108
 
99
109
  .mj-page-header-title {
@@ -114,7 +124,18 @@
114
124
  line-height: 1.4;
115
125
  }
116
126
 
117
- .mj-page-header-meta,
127
+ /* Meta lives in the identity column UNDER the subtitle — supporting info
128
+ for the identity, not a peer to the title. Wraps when there are many
129
+ badges. Matches the mj-page-header-interior pattern so both chrome
130
+ surfaces have one mental model. */
131
+ .mj-page-header-meta {
132
+ display: flex;
133
+ flex-wrap: wrap;
134
+ align-items: center;
135
+ gap: var(--mj-space-2);
136
+ margin-top: var(--mj-space-2);
137
+ }
138
+
118
139
  .mj-page-header-actions {
119
140
  display: flex;
120
141
  align-items: center;
@@ -0,0 +1,96 @@
1
+ import * as i0 from "@angular/core";
2
+ /**
3
+ * `<mj-page-header-interior>` — Interior-chrome sibling of `<mj-page-header>`.
4
+ *
5
+ * Same slot conventions as `<mj-page-header>` (`[meta]` / `[actions]` / `[toolbar]`)
6
+ * so consumers learn one set of names for both surfaces. Different visual shape:
7
+ * a single-row card with surface background + radius + shadow that sits at the
8
+ * top of a sub-page's body, NOT a page-level chrome band.
9
+ *
10
+ * Use this when a sub-page renders inside a left-nav shell that already owns the
11
+ * page-level `<mj-page-header>` — the rail's active-item highlight is the section
12
+ * indicator, so the sub-page doesn't need its own title row; just the filter /
13
+ * action surface in a clean card.
14
+ *
15
+ * ## Layout
16
+ *
17
+ * Two-row card. The primary row has an identity column on the left (Title →
18
+ * Subtitle → Meta, stacked) and an actions cluster on the right; the toolbar
19
+ * row holds dense controls (search, tab nav, filter chips) that would compete
20
+ * with the title for space if they shared a row. When `[toolbar]` has no
21
+ * projected content, the row collapses entirely.
22
+ *
23
+ * ```
24
+ * ┌─────────────────────────────────────────────────────────────────────────┐
25
+ * │ Title [—— spacer ——] [actions] │
26
+ * │ subtitle │
27
+ * │ [meta] │
28
+ * ├─────────────────────────────────────────────────────────────────────────┤
29
+ * │ [ toolbar content … search / tab-nav / filter chips … ] │
30
+ * └─────────────────────────────────────────────────────────────────────────┘
31
+ * ```
32
+ *
33
+ * - **`[Title]`** (input, optional) — short page name. Usually redundant with
34
+ * the parent shell's left-rail label; most sub-pages still set it as a visual
35
+ * anchor.
36
+ * - **`[Subtitle]`** (input, optional) — short prose explaining what the page
37
+ * does. Recommended for pages that aren't self-explanatory from the rail
38
+ * label alone (e.g. Dev Tools inspectors).
39
+ * - **`[meta]`** — **below the subtitle, inside the identity column**: status
40
+ * badges, result counts, stat pills. Reads as supporting info for the
41
+ * identity (not a peer of the title), so the title stays the dominant
42
+ * visual anchor. Wraps to multiple lines when there are many badges.
43
+ * - **`[toolbar]`** — second row: search, tab nav, filter chips, view toggles
44
+ * - **`[actions]`** — top row, right edge: filter popover, refresh, secondary
45
+ * buttons, primary CTA
46
+ *
47
+ * ## Example
48
+ *
49
+ * ```html
50
+ * <mj-page-header-interior Subtitle="Read-only snapshot of Explorer runtime state">
51
+ * <div toolbar>
52
+ * <mj-page-search [Value]="searchTerm" (ValueChange)="onSearch($event)" />
53
+ * <mj-filter-chip Label="Active" [Active]="status === 'active'"
54
+ * (Clicked)="setStatus('active')" />
55
+ * </div>
56
+ * <div actions>
57
+ * <mj-filter-popover ...><mj-filter-panel ...></mj-filter-panel></mj-filter-popover>
58
+ * <mj-refresh-button [Loading]="isLoading" (Clicked)="refresh()" />
59
+ * <button mjButton variant="primary" size="sm">+ Add</button>
60
+ * </div>
61
+ * </mj-page-header-interior>
62
+ * ```
63
+ *
64
+ * See Section 10 of `plans/explorer-chrome-conventions.md` for the full
65
+ * "interior chrome" contract.
66
+ */
67
+ export declare class MJPageHeaderInteriorComponent {
68
+ /**
69
+ * ARIA role applied to the rendered card. Defaults to `'search'` since the
70
+ * dominant use case is a filter / search region. Pass `null` to omit the
71
+ * role attribute (for non-filter chrome — e.g. a settings-only action band).
72
+ */
73
+ Role: string | null;
74
+ /**
75
+ * Accessible label for the rendered card. When set, the card becomes a
76
+ * labeled landmark for assistive tech. Recommended to set this for any
77
+ * chrome that represents a meaningful region (e.g. "Filter users").
78
+ */
79
+ AriaLabel: string | null;
80
+ /**
81
+ * Optional short page name rendered as the identity block on the left of the
82
+ * card. Usually redundant with the parent shell's left-rail label, so most
83
+ * sub-pages omit this and rely on Subtitle alone.
84
+ */
85
+ Title: string | null;
86
+ /**
87
+ * Optional one-line description rendered under Title (or alone, if Title is
88
+ * not set). Recommended for pages whose purpose isn't obvious from the rail
89
+ * label — e.g. Dev Tools inspectors. Keep it short — one line at typical
90
+ * widths.
91
+ */
92
+ Subtitle: string | null;
93
+ static ɵfac: i0.ɵɵFactoryDeclaration<MJPageHeaderInteriorComponent, never>;
94
+ static ɵcmp: i0.ɵɵComponentDeclaration<MJPageHeaderInteriorComponent, "mj-page-header-interior", never, { "Role": { "alias": "Role"; "required": false; }; "AriaLabel": { "alias": "AriaLabel"; "required": false; }; "Title": { "alias": "Title"; "required": false; }; "Subtitle": { "alias": "Subtitle"; "required": false; }; }, {}, never, ["[meta]", "[actions]", "[toolbar]"], true, never>;
95
+ }
96
+ //# sourceMappingURL=page-header-interior.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-header-interior.component.d.ts","sourceRoot":"","sources":["../../../src/lib/page-header-interior/page-header-interior.component.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,qBA2Ka,6BAA6B;IACxC;;;;OAIG;IACM,IAAI,EAAE,MAAM,GAAG,IAAI,CAAY;IAExC;;;;OAIG;IACM,SAAS,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEzC;;;;OAIG;IACM,KAAK,EAAE,MAAM,GAAG,IAAI,CAAQ;IAErC;;;;;OAKG;IACM,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;yCA5B7B,6BAA6B;2CAA7B,6BAA6B;CA6BzC"}