@memberjunction/ng-ui-components 5.34.1 → 5.36.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 (71) hide show
  1. package/dist/lib/button/button.scss +7 -3
  2. package/dist/lib/filter-chip/filter-chip.component.d.ts +31 -0
  3. package/dist/lib/filter-chip/filter-chip.component.d.ts.map +1 -0
  4. package/dist/lib/filter-chip/filter-chip.component.js +94 -0
  5. package/dist/lib/filter-chip/filter-chip.component.js.map +1 -0
  6. package/dist/lib/filter-field/filter-field.component.d.ts +22 -0
  7. package/dist/lib/filter-field/filter-field.component.d.ts.map +1 -0
  8. package/dist/lib/filter-field/filter-field.component.js +60 -0
  9. package/dist/lib/filter-field/filter-field.component.js.map +1 -0
  10. package/dist/lib/filter-panel/filter-panel.component.d.ts +69 -0
  11. package/dist/lib/filter-panel/filter-panel.component.d.ts.map +1 -0
  12. package/dist/lib/filter-panel/filter-panel.component.js +203 -0
  13. package/dist/lib/filter-panel/filter-panel.component.js.map +1 -0
  14. package/dist/lib/filter-popover/filter-popover.component.d.ts +38 -0
  15. package/dist/lib/filter-popover/filter-popover.component.d.ts.map +1 -0
  16. package/dist/lib/filter-popover/filter-popover.component.js +170 -0
  17. package/dist/lib/filter-popover/filter-popover.component.js.map +1 -0
  18. package/dist/lib/left-nav/left-nav-content.component.d.ts +53 -0
  19. package/dist/lib/left-nav/left-nav-content.component.d.ts.map +1 -0
  20. package/dist/lib/left-nav/left-nav-content.component.js +121 -0
  21. package/dist/lib/left-nav/left-nav-content.component.js.map +1 -0
  22. package/dist/lib/left-nav/left-nav.component.d.ts +137 -0
  23. package/dist/lib/left-nav/left-nav.component.d.ts.map +1 -0
  24. package/dist/lib/left-nav/left-nav.component.js +399 -0
  25. package/dist/lib/left-nav/left-nav.component.js.map +1 -0
  26. package/dist/lib/page-body/page-body.component.d.ts +56 -0
  27. package/dist/lib/page-body/page-body.component.d.ts.map +1 -0
  28. package/dist/lib/page-body/page-body.component.js +89 -0
  29. package/dist/lib/page-body/page-body.component.js.map +1 -0
  30. package/dist/lib/page-body-interior/page-body-interior.component.d.ts +62 -0
  31. package/dist/lib/page-body-interior/page-body-interior.component.d.ts.map +1 -0
  32. package/dist/lib/page-body-interior/page-body-interior.component.js +95 -0
  33. package/dist/lib/page-body-interior/page-body-interior.component.js.map +1 -0
  34. package/dist/lib/page-header/page-header.component.d.ts +43 -0
  35. package/dist/lib/page-header/page-header.component.d.ts.map +1 -0
  36. package/dist/lib/page-header/page-header.component.js +126 -0
  37. package/dist/lib/page-header/page-header.component.js.map +1 -0
  38. package/dist/lib/page-header/page-header.scss +160 -0
  39. package/dist/lib/page-header-interior/page-header-interior.component.d.ts +96 -0
  40. package/dist/lib/page-header-interior/page-header-interior.component.d.ts.map +1 -0
  41. package/dist/lib/page-header-interior/page-header-interior.component.js +176 -0
  42. package/dist/lib/page-header-interior/page-header-interior.component.js.map +1 -0
  43. package/dist/lib/page-layout/page-layout.component.d.ts +20 -0
  44. package/dist/lib/page-layout/page-layout.component.d.ts.map +1 -0
  45. package/dist/lib/page-layout/page-layout.component.js +30 -0
  46. package/dist/lib/page-layout/page-layout.component.js.map +1 -0
  47. package/dist/lib/page-search/page-search.component.d.ts +29 -0
  48. package/dist/lib/page-search/page-search.component.d.ts.map +1 -0
  49. package/dist/lib/page-search/page-search.component.js +69 -0
  50. package/dist/lib/page-search/page-search.component.js.map +1 -0
  51. package/dist/lib/refresh-button/refresh-button.component.d.ts +46 -0
  52. package/dist/lib/refresh-button/refresh-button.component.d.ts.map +1 -0
  53. package/dist/lib/refresh-button/refresh-button.component.js +106 -0
  54. package/dist/lib/refresh-button/refresh-button.component.js.map +1 -0
  55. package/dist/lib/stat-badge/stat-badge.component.d.ts +54 -0
  56. package/dist/lib/stat-badge/stat-badge.component.d.ts.map +1 -0
  57. package/dist/lib/stat-badge/stat-badge.component.js +162 -0
  58. package/dist/lib/stat-badge/stat-badge.component.js.map +1 -0
  59. package/dist/lib/tab-nav/tab-nav.component.d.ts +41 -0
  60. package/dist/lib/tab-nav/tab-nav.component.d.ts.map +1 -0
  61. package/dist/lib/tab-nav/tab-nav.component.js +111 -0
  62. package/dist/lib/tab-nav/tab-nav.component.js.map +1 -0
  63. package/dist/lib/view-toggle/view-toggle.component.d.ts +42 -0
  64. package/dist/lib/view-toggle/view-toggle.component.d.ts.map +1 -0
  65. package/dist/lib/view-toggle/view-toggle.component.js +104 -0
  66. package/dist/lib/view-toggle/view-toggle.component.js.map +1 -0
  67. package/dist/public-api.d.ts +16 -0
  68. package/dist/public-api.d.ts.map +1 -1
  69. package/dist/public-api.js +16 -0
  70. package/dist/public-api.js.map +1 -1
  71. package/package.json +2 -2
@@ -0,0 +1,176 @@
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 MJPageHeaderInteriorComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
6
+ i0.ɵɵdomElementStart(0, "div", 3);
7
+ i0.ɵɵtext(1);
8
+ i0.ɵɵdomElementEnd();
9
+ } if (rf & 2) {
10
+ const ctx_r0 = i0.ɵɵnextContext();
11
+ i0.ɵɵadvance();
12
+ i0.ɵɵtextInterpolate(ctx_r0.Title);
13
+ } }
14
+ function MJPageHeaderInteriorComponent_Conditional_4_Template(rf, ctx) { if (rf & 1) {
15
+ i0.ɵɵdomElementStart(0, "div", 4);
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-interior>` — Interior-chrome sibling of `<mj-page-header>`.
25
+ *
26
+ * Same slot conventions as `<mj-page-header>` (`[meta]` / `[actions]` / `[toolbar]`)
27
+ * so consumers learn one set of names for both surfaces. Different visual shape:
28
+ * a single-row card with surface background + radius + shadow that sits at the
29
+ * top of a sub-page's body, NOT a page-level chrome band.
30
+ *
31
+ * Use this when a sub-page renders inside a left-nav shell that already owns the
32
+ * page-level `<mj-page-header>` — the rail's active-item highlight is the section
33
+ * indicator, so the sub-page doesn't need its own title row; just the filter /
34
+ * action surface in a clean card.
35
+ *
36
+ * ## Layout
37
+ *
38
+ * Two-row card. The primary row has an identity column on the left (Title →
39
+ * Subtitle → Meta, stacked) and an actions cluster on the right; the toolbar
40
+ * row holds dense controls (search, tab nav, filter chips) that would compete
41
+ * with the title for space if they shared a row. When `[toolbar]` has no
42
+ * projected content, the row collapses entirely.
43
+ *
44
+ * ```
45
+ * ┌─────────────────────────────────────────────────────────────────────────┐
46
+ * │ Title [—— spacer ——] [actions] │
47
+ * │ subtitle │
48
+ * │ [meta] │
49
+ * ├─────────────────────────────────────────────────────────────────────────┤
50
+ * │ [ toolbar content … search / tab-nav / filter chips … ] │
51
+ * └─────────────────────────────────────────────────────────────────────────┘
52
+ * ```
53
+ *
54
+ * - **`[Title]`** (input, optional) — short page name. Usually redundant with
55
+ * the parent shell's left-rail label; most sub-pages still set it as a visual
56
+ * anchor.
57
+ * - **`[Subtitle]`** (input, optional) — short prose explaining what the page
58
+ * does. Recommended for pages that aren't self-explanatory from the rail
59
+ * label alone (e.g. Dev Tools inspectors).
60
+ * - **`[meta]`** — **below the subtitle, inside the identity column**: status
61
+ * badges, result counts, stat pills. Reads as supporting info for the
62
+ * identity (not a peer of the title), so the title stays the dominant
63
+ * visual anchor. Wraps to multiple lines when there are many badges.
64
+ * - **`[toolbar]`** — second row: search, tab nav, filter chips, view toggles
65
+ * - **`[actions]`** — top row, right edge: filter popover, refresh, secondary
66
+ * buttons, primary CTA
67
+ *
68
+ * ## Example
69
+ *
70
+ * ```html
71
+ * <mj-page-header-interior Subtitle="Read-only snapshot of Explorer runtime state">
72
+ * <div toolbar>
73
+ * <mj-page-search [Value]="searchTerm" (ValueChange)="onSearch($event)" />
74
+ * <mj-filter-chip Label="Active" [Active]="status === 'active'"
75
+ * (Clicked)="setStatus('active')" />
76
+ * </div>
77
+ * <div actions>
78
+ * <mj-filter-popover ...><mj-filter-panel ...></mj-filter-panel></mj-filter-popover>
79
+ * <mj-refresh-button [Loading]="isLoading" (Clicked)="refresh()" />
80
+ * <button mjButton variant="primary" size="sm">+ Add</button>
81
+ * </div>
82
+ * </mj-page-header-interior>
83
+ * ```
84
+ *
85
+ * See Section 10 of `plans/explorer-chrome-conventions.md` for the full
86
+ * "interior chrome" contract.
87
+ */
88
+ export class MJPageHeaderInteriorComponent {
89
+ /**
90
+ * ARIA role applied to the rendered card. Defaults to `'search'` since the
91
+ * dominant use case is a filter / search region. Pass `null` to omit the
92
+ * role attribute (for non-filter chrome — e.g. a settings-only action band).
93
+ */
94
+ Role = 'search';
95
+ /**
96
+ * Accessible label for the rendered card. When set, the card becomes a
97
+ * labeled landmark for assistive tech. Recommended to set this for any
98
+ * chrome that represents a meaningful region (e.g. "Filter users").
99
+ */
100
+ AriaLabel = null;
101
+ /**
102
+ * Optional short page name rendered as the identity block on the left of the
103
+ * card. Usually redundant with the parent shell's left-rail label, so most
104
+ * sub-pages omit this and rely on Subtitle alone.
105
+ */
106
+ Title = null;
107
+ /**
108
+ * Optional one-line description rendered under Title (or alone, if Title is
109
+ * not set). Recommended for pages whose purpose isn't obvious from the rail
110
+ * label — e.g. Dev Tools inspectors. Keep it short — one line at typical
111
+ * widths.
112
+ */
113
+ Subtitle = null;
114
+ static ɵfac = function MJPageHeaderInteriorComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MJPageHeaderInteriorComponent)(); };
115
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJPageHeaderInteriorComponent, selectors: [["mj-page-header-interior"]], inputs: { Role: "Role", AriaLabel: "AriaLabel", Title: "Title", Subtitle: "Subtitle" }, ngContentSelectors: _c1, decls: 12, vars: 4, consts: [[1, "mj-page-header-interior"], [1, "mj-page-header-interior__row", "mj-page-header-interior__row--primary"], [1, "mj-page-header-interior__identity"], [1, "mj-page-header-interior__title"], [1, "mj-page-header-interior__subtitle"], [1, "mj-page-header-interior__meta"], [1, "mj-page-header-interior__spacer"], [1, "mj-page-header-interior__actions"], [1, "mj-page-header-interior__row", "mj-page-header-interior__row--toolbar"]], template: function MJPageHeaderInteriorComponent_Template(rf, ctx) { if (rf & 1) {
116
+ i0.ɵɵprojectionDef(_c0);
117
+ i0.ɵɵdomElementStart(0, "div", 0)(1, "div", 1)(2, "div", 2);
118
+ i0.ɵɵconditionalCreate(3, MJPageHeaderInteriorComponent_Conditional_3_Template, 2, 1, "div", 3);
119
+ i0.ɵɵconditionalCreate(4, MJPageHeaderInteriorComponent_Conditional_4_Template, 2, 1, "div", 4);
120
+ i0.ɵɵdomElementStart(5, "div", 5);
121
+ i0.ɵɵprojection(6);
122
+ i0.ɵɵdomElementEnd()();
123
+ i0.ɵɵdomElement(7, "div", 6);
124
+ i0.ɵɵdomElementStart(8, "div", 7);
125
+ i0.ɵɵprojection(9, 1);
126
+ i0.ɵɵdomElementEnd()();
127
+ i0.ɵɵdomElementStart(10, "div", 8);
128
+ i0.ɵɵprojection(11, 2);
129
+ i0.ɵɵdomElementEnd()();
130
+ } if (rf & 2) {
131
+ i0.ɵɵattribute("role", ctx.Role)("aria-label", ctx.AriaLabel);
132
+ i0.ɵɵadvance(3);
133
+ i0.ɵɵconditional(ctx.Title ? 3 : -1);
134
+ i0.ɵɵadvance();
135
+ i0.ɵɵconditional(ctx.Subtitle ? 4 : -1);
136
+ } }, styles: ["[_nghost-%COMP%] {\n display: block;\n margin: var(--mj-space-4) var(--mj-space-4) var(--mj-space-3);\n }\n\n .mj-page-header-interior[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\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\n \n\n\n\n\n\n .mj-page-header-interior__row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n padding: var(--mj-space-3) var(--mj-space-4);\n }\n\n .mj-page-header-interior__row--primary[_ngcontent-%COMP%] {\n gap: var(--mj-space-4);\n }\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n .mj-page-header-interior__row--toolbar[_ngcontent-%COMP%] {\n display: none;\n }\n\n \n\n\n\n\n [_nghost-%COMP%] .mj-page-header-interior__row--toolbar:has(> [toolbar] > *) {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--mj-space-3);\n padding: var(--mj-space-3) var(--mj-space-4);\n border-top: 1px solid var(--mj-border-subtle);\n }\n\n .mj-page-header-interior__identity[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: 2px;\n flex-shrink: 1;\n min-width: 0;\n }\n\n \n\n\n\n .mj-page-header-interior__title[_ngcontent-%COMP%] {\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 }\n .mj-page-header-interior__subtitle[_ngcontent-%COMP%] {\n font-size: var(--mj-text-sm);\n color: var(--mj-text-secondary);\n line-height: 1.4;\n }\n\n .mj-page-header-interior__spacer[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-width: 0;\n }\n\n \n\n\n\n\n\n\n .mj-page-header-interior__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-interior__actions[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: var(--mj-space-3);\n flex-shrink: 0;\n }\n\n \n\n .mj-page-header-interior__meta[_ngcontent-%COMP%]:empty, \n .mj-page-header-interior__actions[_ngcontent-%COMP%]:empty {\n display: none;\n }\n\n \n\n\n\n .mj-page-header-interior__identity[_ngcontent-%COMP%]:not(:has(.mj-page-header-interior__title)):not(:has(.mj-page-header-interior__subtitle)):has(.mj-page-header-interior__meta:empty) {\n display: none;\n }\n\n \n\n\n"] });
137
+ }
138
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJPageHeaderInteriorComponent, [{
139
+ type: Component,
140
+ args: [{ selector: 'mj-page-header-interior', standalone: true, template: `
141
+ <div class="mj-page-header-interior"
142
+ [attr.role]="Role"
143
+ [attr.aria-label]="AriaLabel">
144
+ <div class="mj-page-header-interior__row mj-page-header-interior__row--primary">
145
+ <div class="mj-page-header-interior__identity">
146
+ @if (Title) {
147
+ <div class="mj-page-header-interior__title">{{ Title }}</div>
148
+ }
149
+ @if (Subtitle) {
150
+ <div class="mj-page-header-interior__subtitle">{{ Subtitle }}</div>
151
+ }
152
+ <div class="mj-page-header-interior__meta">
153
+ <ng-content select="[meta]"></ng-content>
154
+ </div>
155
+ </div>
156
+ <div class="mj-page-header-interior__spacer"></div>
157
+ <div class="mj-page-header-interior__actions">
158
+ <ng-content select="[actions]"></ng-content>
159
+ </div>
160
+ </div>
161
+ <div class="mj-page-header-interior__row mj-page-header-interior__row--toolbar">
162
+ <ng-content select="[toolbar]"></ng-content>
163
+ </div>
164
+ </div>
165
+ `, styles: ["\n :host {\n display: block;\n margin: var(--mj-space-4) var(--mj-space-4) var(--mj-space-3);\n }\n\n .mj-page-header-interior {\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n }\n\n /* Slot-wrapper passthrough \u2014 any element a consumer projects with the\n `meta`, `actions`, or `toolbar` attribute (regardless of class name)\n becomes a transparent container so its children become direct flex\n children of the row wrapper and inherit the row's gap. Matches the\n same pattern <mj-page-header> uses. */\n :host ::ng-deep [meta],\n :host ::ng-deep [actions],\n :host ::ng-deep [toolbar] {\n display: contents;\n }\n\n\n /* Rows: primary (identity + actions) and toolbar (separate row).\n Primary row centers actions vertically against the identity column\n (title \u2192 subtitle \u2192 optional meta). When meta is present the actions\n drift down slightly \u2014 acceptable trade for the more common no-meta\n case where flex-start looks top-heavy. */\n .mj-page-header-interior__row {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n padding: var(--mj-space-3) var(--mj-space-4);\n }\n\n .mj-page-header-interior__row--primary {\n gap: var(--mj-space-4);\n }\n\n /* Toolbar row \u2014 hidden by default, opt-in via consumer projection.\n\n Defaulting to display:none (instead of \"show then hide when empty\")\n eliminates the dangling divider + empty band on sub-pages that have\n no toolbar content (Dev Tools inspectors, action-only sections, etc.).\n The row only appears when the consumer projects a [toolbar] element\n that actually has content.\n\n ::ng-deep is required because the projected [toolbar] wrapper carries\n the CONSUMER's view-encapsulation scope, not ours \u2014 a scoped selector\n for [toolbar] would never match the projected wrapper, so we have to\n escape our scope to see it. The matched-content rule restores all\n the row's intended styling (flex layout, padding, divider). */\n .mj-page-header-interior__row--toolbar {\n display: none;\n }\n\n /* Nested :has(... :has(...)) is disallowed by browsers (Chromium throws\n SyntaxError; the rule is silently dropped). Use a single-level :has\n that walks two generations directly: \"toolbar row containing a [toolbar]\n wrapper that itself has any element child\". */\n :host ::ng-deep .mj-page-header-interior__row--toolbar:has(> [toolbar] > *) {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--mj-space-3);\n padding: var(--mj-space-3) var(--mj-space-4);\n border-top: 1px solid var(--mj-border-subtle);\n }\n\n .mj-page-header-interior__identity {\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: 2px;\n flex-shrink: 1;\n min-width: 0;\n }\n\n /* Identity typography matches <mj-page-header> exactly \u2014 same title size,\n same subtitle size, same colors. One mental model across both chrome\n surfaces. */\n .mj-page-header-interior__title {\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 }\n .mj-page-header-interior__subtitle {\n font-size: var(--mj-text-sm);\n color: var(--mj-text-secondary);\n line-height: 1.4;\n }\n\n .mj-page-header-interior__spacer {\n flex: 1 1 auto;\n min-width: 0;\n }\n\n /* Meta lives in the identity column UNDER the subtitle \u2014 it's supporting\n info for the identity, not a peer to the title. Wraps when there are\n many badges so it doesn't force horizontal overflow. The top margin\n gives the badge row clear separation from the subtitle so it reads as\n a distinct band of supporting data rather than a continuation of the\n prose line above. */\n .mj-page-header-interior__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-interior__actions {\n display: inline-flex;\n align-items: center;\n gap: var(--mj-space-3);\n flex-shrink: 0;\n }\n\n /* Collapse the meta + actions wrappers when there's nothing projected. */\n .mj-page-header-interior__meta:empty,\n .mj-page-header-interior__actions:empty {\n display: none;\n }\n\n /* Collapse the identity column entirely when title/subtitle aren't set AND\n no [meta] content is projected \u2014 covers the action-band-only sub-pages\n (Dev Tools inspectors, SQL Logging) where the card is pure actions. */\n .mj-page-header-interior__identity:not(:has(.mj-page-header-interior__title)):not(:has(.mj-page-header-interior__subtitle)):has(.mj-page-header-interior__meta:empty) {\n display: none;\n }\n\n /* No .mj-btn reset here. Buttons projected into the chrome inherit the\n mjButton directive's global styles from button.scss \u2014 see \"Button Styling\"\n in packages/Angular/CLAUDE.md. */\n "] }]
166
+ }], null, { Role: [{
167
+ type: Input
168
+ }], AriaLabel: [{
169
+ type: Input
170
+ }], Title: [{
171
+ type: Input
172
+ }], Subtitle: [{
173
+ type: Input
174
+ }] }); })();
175
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJPageHeaderInteriorComponent, { className: "MJPageHeaderInteriorComponent", filePath: "lib/page-header-interior/page-header-interior.component.ts", lineNumber: 239 }); })();
176
+ //# sourceMappingURL=page-header-interior.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-header-interior.component.js","sourceRoot":"","sources":["../../../src/lib/page-header-interior/page-header-interior.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;;;;;IA6ErC,iCAA4C;IAAA,YAAW;IAAA,oBAAM;;;IAAjB,cAAW;IAAX,kCAAW;;;IAGvD,iCAA+C;IAAA,YAAc;IAAA,oBAAM;;;IAApB,cAAc;IAAd,qCAAc;;AA9EzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AA4KH,MAAM,OAAO,6BAA6B;IACxC;;;;OAIG;IACM,IAAI,GAAkB,QAAQ,CAAC;IAExC;;;;OAIG;IACM,SAAS,GAAkB,IAAI,CAAC;IAEzC;;;;OAIG;IACM,KAAK,GAAkB,IAAI,CAAC;IAErC;;;;;OAKG;IACM,QAAQ,GAAkB,IAAI,CAAC;uHA5B7B,6BAA6B;6DAA7B,6BAA6B;;YAnKlC,AADF,AAHF,iCAEmC,aAC+C,aAC/B;YAC7C,+FAAa;YAGb,+FAAgB;YAGhB,iCAA2C;YACzC,kBAAyC;YAE7C,AADE,oBAAM,EACF;YACN,4BAAmD;YACnD,iCAA8C;YAC5C,qBAA4C;YAEhD,AADE,oBAAM,EACF;YACN,kCAAgF;YAC9E,sBAA4C;YAEhD,AADE,oBAAM,EACF;;;YAlBA,eAEC;YAFD,oCAEC;YACD,cAEC;YAFD,uCAEC;;;iFA6JE,6BAA6B;cA3KzC,SAAS;2BACE,yBAAyB,cACvB,IAAI,YACN;;;;;;;;;;;;;;;;;;;;;;;;;GAyBT;;kBAqJA,KAAK;;kBAOL,KAAK;;kBAOL,KAAK;;kBAQL,KAAK;;kFA5BK,6BAA6B","sourcesContent":["import { Component, Input } from '@angular/core';\n\n/**\n * `<mj-page-header-interior>` — Interior-chrome sibling of `<mj-page-header>`.\n *\n * Same slot conventions as `<mj-page-header>` (`[meta]` / `[actions]` / `[toolbar]`)\n * so consumers learn one set of names for both surfaces. Different visual shape:\n * a single-row card with surface background + radius + shadow that sits at the\n * top of a sub-page's body, NOT a page-level chrome band.\n *\n * Use this when a sub-page renders inside a left-nav shell that already owns the\n * page-level `<mj-page-header>` — the rail's active-item highlight is the section\n * indicator, so the sub-page doesn't need its own title row; just the filter /\n * action surface in a clean card.\n *\n * ## Layout\n *\n * Two-row card. The primary row has an identity column on the left (Title →\n * Subtitle → Meta, stacked) and an actions cluster on the right; the toolbar\n * row holds dense controls (search, tab nav, filter chips) that would compete\n * with the title for space if they shared a row. When `[toolbar]` has no\n * projected content, the row collapses entirely.\n *\n * ```\n * ┌─────────────────────────────────────────────────────────────────────────┐\n * │ Title [—— spacer ——] [actions] │\n * │ subtitle │\n * │ [meta] │\n * ├─────────────────────────────────────────────────────────────────────────┤\n * │ [ toolbar content … search / tab-nav / filter chips … ] │\n * └─────────────────────────────────────────────────────────────────────────┘\n * ```\n *\n * - **`[Title]`** (input, optional) — short page name. Usually redundant with\n * the parent shell's left-rail label; most sub-pages still set it as a visual\n * anchor.\n * - **`[Subtitle]`** (input, optional) — short prose explaining what the page\n * does. Recommended for pages that aren't self-explanatory from the rail\n * label alone (e.g. Dev Tools inspectors).\n * - **`[meta]`** — **below the subtitle, inside the identity column**: status\n * badges, result counts, stat pills. Reads as supporting info for the\n * identity (not a peer of the title), so the title stays the dominant\n * visual anchor. Wraps to multiple lines when there are many badges.\n * - **`[toolbar]`** — second row: search, tab nav, filter chips, view toggles\n * - **`[actions]`** — top row, right edge: filter popover, refresh, secondary\n * buttons, primary CTA\n *\n * ## Example\n *\n * ```html\n * <mj-page-header-interior Subtitle=\"Read-only snapshot of Explorer runtime state\">\n * <div toolbar>\n * <mj-page-search [Value]=\"searchTerm\" (ValueChange)=\"onSearch($event)\" />\n * <mj-filter-chip Label=\"Active\" [Active]=\"status === 'active'\"\n * (Clicked)=\"setStatus('active')\" />\n * </div>\n * <div actions>\n * <mj-filter-popover ...><mj-filter-panel ...></mj-filter-panel></mj-filter-popover>\n * <mj-refresh-button [Loading]=\"isLoading\" (Clicked)=\"refresh()\" />\n * <button mjButton variant=\"primary\" size=\"sm\">+ Add</button>\n * </div>\n * </mj-page-header-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-header-interior',\n standalone: true,\n template: `\n <div class=\"mj-page-header-interior\"\n [attr.role]=\"Role\"\n [attr.aria-label]=\"AriaLabel\">\n <div class=\"mj-page-header-interior__row mj-page-header-interior__row--primary\">\n <div class=\"mj-page-header-interior__identity\">\n @if (Title) {\n <div class=\"mj-page-header-interior__title\">{{ Title }}</div>\n }\n @if (Subtitle) {\n <div class=\"mj-page-header-interior__subtitle\">{{ Subtitle }}</div>\n }\n <div class=\"mj-page-header-interior__meta\">\n <ng-content select=\"[meta]\"></ng-content>\n </div>\n </div>\n <div class=\"mj-page-header-interior__spacer\"></div>\n <div class=\"mj-page-header-interior__actions\">\n <ng-content select=\"[actions]\"></ng-content>\n </div>\n </div>\n <div class=\"mj-page-header-interior__row mj-page-header-interior__row--toolbar\">\n <ng-content select=\"[toolbar]\"></ng-content>\n </div>\n </div>\n `,\n styles: [`\n :host {\n display: block;\n margin: var(--mj-space-4) var(--mj-space-4) var(--mj-space-3);\n }\n\n .mj-page-header-interior {\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n }\n\n /* Slot-wrapper passthrough — any element a consumer projects with the\n \\`meta\\`, \\`actions\\`, or \\`toolbar\\` attribute (regardless of class name)\n becomes a transparent container so its children become direct flex\n children of the row wrapper and inherit the row's gap. Matches the\n same pattern <mj-page-header> uses. */\n :host ::ng-deep [meta],\n :host ::ng-deep [actions],\n :host ::ng-deep [toolbar] {\n display: contents;\n }\n\n\n /* Rows: primary (identity + actions) and toolbar (separate row).\n Primary row centers actions vertically against the identity column\n (title → subtitle → optional meta). When meta is present the actions\n drift down slightly — acceptable trade for the more common no-meta\n case where flex-start looks top-heavy. */\n .mj-page-header-interior__row {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n padding: var(--mj-space-3) var(--mj-space-4);\n }\n\n .mj-page-header-interior__row--primary {\n gap: var(--mj-space-4);\n }\n\n /* Toolbar row — hidden by default, opt-in via consumer projection.\n\n Defaulting to display:none (instead of \"show then hide when empty\")\n eliminates the dangling divider + empty band on sub-pages that have\n no toolbar content (Dev Tools inspectors, action-only sections, etc.).\n The row only appears when the consumer projects a [toolbar] element\n that actually has content.\n\n ::ng-deep is required because the projected [toolbar] wrapper carries\n the CONSUMER's view-encapsulation scope, not ours — a scoped selector\n for [toolbar] would never match the projected wrapper, so we have to\n escape our scope to see it. The matched-content rule restores all\n the row's intended styling (flex layout, padding, divider). */\n .mj-page-header-interior__row--toolbar {\n display: none;\n }\n\n /* Nested :has(... :has(...)) is disallowed by browsers (Chromium throws\n SyntaxError; the rule is silently dropped). Use a single-level :has\n that walks two generations directly: \"toolbar row containing a [toolbar]\n wrapper that itself has any element child\". */\n :host ::ng-deep .mj-page-header-interior__row--toolbar:has(> [toolbar] > *) {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--mj-space-3);\n padding: var(--mj-space-3) var(--mj-space-4);\n border-top: 1px solid var(--mj-border-subtle);\n }\n\n .mj-page-header-interior__identity {\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: 2px;\n flex-shrink: 1;\n min-width: 0;\n }\n\n /* Identity typography matches <mj-page-header> exactly — same title size,\n same subtitle size, same colors. One mental model across both chrome\n surfaces. */\n .mj-page-header-interior__title {\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 }\n .mj-page-header-interior__subtitle {\n font-size: var(--mj-text-sm);\n color: var(--mj-text-secondary);\n line-height: 1.4;\n }\n\n .mj-page-header-interior__spacer {\n flex: 1 1 auto;\n min-width: 0;\n }\n\n /* Meta lives in the identity column UNDER the subtitle — it's supporting\n info for the identity, not a peer to the title. Wraps when there are\n many badges so it doesn't force horizontal overflow. The top margin\n gives the badge row clear separation from the subtitle so it reads as\n a distinct band of supporting data rather than a continuation of the\n prose line above. */\n .mj-page-header-interior__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-interior__actions {\n display: inline-flex;\n align-items: center;\n gap: var(--mj-space-3);\n flex-shrink: 0;\n }\n\n /* Collapse the meta + actions wrappers when there's nothing projected. */\n .mj-page-header-interior__meta:empty,\n .mj-page-header-interior__actions:empty {\n display: none;\n }\n\n /* Collapse the identity column entirely when title/subtitle aren't set AND\n no [meta] content is projected — covers the action-band-only sub-pages\n (Dev Tools inspectors, SQL Logging) where the card is pure actions. */\n .mj-page-header-interior__identity:not(:has(.mj-page-header-interior__title)):not(:has(.mj-page-header-interior__subtitle)):has(.mj-page-header-interior__meta:empty) {\n display: none;\n }\n\n /* No .mj-btn reset here. Buttons projected into the chrome inherit the\n mjButton directive's global styles from button.scss — see \"Button Styling\"\n in packages/Angular/CLAUDE.md. */\n `]\n})\nexport class MJPageHeaderInteriorComponent {\n /**\n * ARIA role applied to the rendered card. Defaults to `'search'` since the\n * dominant use case is a filter / search region. Pass `null` to omit the\n * role attribute (for non-filter chrome — e.g. a settings-only action band).\n */\n @Input() Role: string | null = 'search';\n\n /**\n * Accessible label for the rendered card. When set, the card becomes a\n * labeled landmark for assistive tech. Recommended to set this for any\n * chrome that represents a meaningful region (e.g. \"Filter users\").\n */\n @Input() AriaLabel: string | null = null;\n\n /**\n * Optional short page name rendered as the identity block on the left of the\n * card. Usually redundant with the parent shell's left-rail label, so most\n * sub-pages omit this and rely on Subtitle alone.\n */\n @Input() Title: string | null = null;\n\n /**\n * Optional one-line description rendered under Title (or alone, if Title is\n * not set). Recommended for pages whose purpose isn't obvious from the rail\n * label — e.g. Dev Tools inspectors. Keep it short — one line at typical\n * widths.\n */\n @Input() Subtitle: string | null = null;\n}\n"]}
@@ -0,0 +1,20 @@
1
+ import * as i0 from "@angular/core";
2
+ /**
3
+ * mj-page-layout — Canonical outer shell for MJ Explorer dashboards/resource pages.
4
+ *
5
+ * Provides the standard flex-column / full-height / page-background container.
6
+ * Place a `<mj-page-header>` and the page body inside.
7
+ *
8
+ * Example:
9
+ * ```html
10
+ * <mj-page-layout>
11
+ * <mj-page-header Title="Agents" Icon="fa-solid fa-robot">...</mj-page-header>
12
+ * <!-- page content -->
13
+ * </mj-page-layout>
14
+ * ```
15
+ */
16
+ export declare class MJPageLayoutComponent {
17
+ static ɵfac: i0.ɵɵFactoryDeclaration<MJPageLayoutComponent, never>;
18
+ static ɵcmp: i0.ɵɵComponentDeclaration<MJPageLayoutComponent, "mj-page-layout", never, {}, {}, never, ["*"], true, never>;
19
+ }
20
+ //# sourceMappingURL=page-layout.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-layout.component.d.ts","sourceRoot":"","sources":["../../../src/lib/page-layout/page-layout.component.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;GAaG;AACH,qBAca,qBAAqB;yCAArB,qBAAqB;2CAArB,qBAAqB;CAAG"}
@@ -0,0 +1,30 @@
1
+ import { Component } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ const _c0 = ["*"];
4
+ /**
5
+ * mj-page-layout — Canonical outer shell for MJ Explorer dashboards/resource pages.
6
+ *
7
+ * Provides the standard flex-column / full-height / page-background container.
8
+ * Place a `<mj-page-header>` and the page body inside.
9
+ *
10
+ * Example:
11
+ * ```html
12
+ * <mj-page-layout>
13
+ * <mj-page-header Title="Agents" Icon="fa-solid fa-robot">...</mj-page-header>
14
+ * <!-- page content -->
15
+ * </mj-page-layout>
16
+ * ```
17
+ */
18
+ export class MJPageLayoutComponent {
19
+ static ɵfac = function MJPageLayoutComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MJPageLayoutComponent)(); };
20
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJPageLayoutComponent, selectors: [["mj-page-layout"]], ngContentSelectors: _c0, decls: 1, vars: 0, template: function MJPageLayoutComponent_Template(rf, ctx) { if (rf & 1) {
21
+ i0.ɵɵprojectionDef();
22
+ i0.ɵɵprojection(0);
23
+ } }, styles: ["[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-page);\n overflow: hidden;\n }"] });
24
+ }
25
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJPageLayoutComponent, [{
26
+ type: Component,
27
+ args: [{ selector: 'mj-page-layout', standalone: true, template: `<ng-content></ng-content>`, styles: ["\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-page);\n overflow: hidden;\n }\n "] }]
28
+ }], null, null); })();
29
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJPageLayoutComponent, { className: "MJPageLayoutComponent", filePath: "lib/page-layout/page-layout.component.ts", lineNumber: 31 }); })();
30
+ //# sourceMappingURL=page-layout.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-layout.component.js","sourceRoot":"","sources":["../../../src/lib/page-layout/page-layout.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;;;AAE1C;;;;;;;;;;;;;GAaG;AAeH,MAAM,OAAO,qBAAqB;+GAArB,qBAAqB;6DAArB,qBAAqB;;YAXrB,kBAAyB;;;iFAWzB,qBAAqB;cAdjC,SAAS;2BACE,gBAAgB,cACd,IAAI,YACN,2BAA2B;;kFAW1B,qBAAqB","sourcesContent":["import { Component } from '@angular/core';\n\n/**\n * mj-page-layout — Canonical outer shell for MJ Explorer dashboards/resource pages.\n *\n * Provides the standard flex-column / full-height / page-background container.\n * Place a `<mj-page-header>` and the page body inside.\n *\n * Example:\n * ```html\n * <mj-page-layout>\n * <mj-page-header Title=\"Agents\" Icon=\"fa-solid fa-robot\">...</mj-page-header>\n * <!-- page content -->\n * </mj-page-layout>\n * ```\n */\n@Component({\n selector: 'mj-page-layout',\n standalone: true,\n template: `<ng-content></ng-content>`,\n styles: [`\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-page);\n overflow: hidden;\n }\n `]\n})\nexport class MJPageLayoutComponent {}\n"]}
@@ -0,0 +1,29 @@
1
+ import { EventEmitter } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ /**
4
+ * mj-page-search — Canonical in-page search input for dashboard headers and toolbars.
5
+ * Replaces per-page `.search-input-wrapper` rules so styling can't drift.
6
+ *
7
+ * Distinct from `mj-search-input` (the navbar/global search in `@memberjunction/ng-search`)
8
+ * — this one is a simple text input used for filtering page content.
9
+ *
10
+ * Example:
11
+ * ```html
12
+ * <mj-page-search
13
+ * Placeholder="Search templates..."
14
+ * [Value]="searchTerm"
15
+ * (ValueChange)="onSearch($event)">
16
+ * </mj-page-search>
17
+ * ```
18
+ */
19
+ export declare class MJPageSearchComponent {
20
+ Placeholder: string;
21
+ Value: string;
22
+ Icon: string;
23
+ ValueChange: EventEmitter<string>;
24
+ focused: boolean;
25
+ onInput(event: Event): void;
26
+ static ɵfac: i0.ɵɵFactoryDeclaration<MJPageSearchComponent, never>;
27
+ static ɵcmp: i0.ɵɵComponentDeclaration<MJPageSearchComponent, "mj-page-search", never, { "Placeholder": { "alias": "Placeholder"; "required": false; }; "Value": { "alias": "Value"; "required": false; }; "Icon": { "alias": "Icon"; "required": false; }; }, { "ValueChange": "ValueChange"; }, never, never, true, never>;
28
+ }
29
+ //# sourceMappingURL=page-search.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-search.component.d.ts","sourceRoot":"","sources":["../../../src/lib/page-search/page-search.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAiB,MAAM,eAAe,CAAC;;AAEvE;;;;;;;;;;;;;;;GAeG;AACH,qBA0Ea,qBAAqB;IACvB,WAAW,EAAE,MAAM,CAAe;IAClC,KAAK,EAAE,MAAM,CAAM;IACnB,IAAI,EAAE,MAAM,CAAwB;IACnC,WAAW,uBAA8B;IAE5C,OAAO,EAAE,OAAO,CAAS;IAEzB,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;yCARvB,qBAAqB;2CAArB,qBAAqB;CAajC"}
@@ -0,0 +1,69 @@
1
+ import { Component, EventEmitter, Input, Output } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ /**
4
+ * mj-page-search — Canonical in-page search input for dashboard headers and toolbars.
5
+ * Replaces per-page `.search-input-wrapper` rules so styling can't drift.
6
+ *
7
+ * Distinct from `mj-search-input` (the navbar/global search in `@memberjunction/ng-search`)
8
+ * — this one is a simple text input used for filtering page content.
9
+ *
10
+ * Example:
11
+ * ```html
12
+ * <mj-page-search
13
+ * Placeholder="Search templates..."
14
+ * [Value]="searchTerm"
15
+ * (ValueChange)="onSearch($event)">
16
+ * </mj-page-search>
17
+ * ```
18
+ */
19
+ export class MJPageSearchComponent {
20
+ Placeholder = 'Search...';
21
+ Value = '';
22
+ Icon = 'fa-solid fa-search';
23
+ ValueChange = new EventEmitter();
24
+ focused = false;
25
+ onInput(event) {
26
+ const v = event.target.value;
27
+ this.Value = v;
28
+ this.ValueChange.emit(v);
29
+ }
30
+ static ɵfac = function MJPageSearchComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MJPageSearchComponent)(); };
31
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJPageSearchComponent, selectors: [["mj-page-search"]], inputs: { Placeholder: "Placeholder", Value: "Value", Icon: "Icon" }, outputs: { ValueChange: "ValueChange" }, decls: 3, vars: 6, consts: [[1, "mj-page-search"], ["type", "text", 3, "input", "focus", "blur", "placeholder", "value"]], template: function MJPageSearchComponent_Template(rf, ctx) { if (rf & 1) {
32
+ i0.ɵɵdomElementStart(0, "div", 0);
33
+ i0.ɵɵdomElement(1, "i");
34
+ i0.ɵɵdomElementStart(2, "input", 1);
35
+ i0.ɵɵdomListener("input", function MJPageSearchComponent_Template_input_input_2_listener($event) { return ctx.onInput($event); })("focus", function MJPageSearchComponent_Template_input_focus_2_listener() { return ctx.focused = true; })("blur", function MJPageSearchComponent_Template_input_blur_2_listener() { return ctx.focused = false; });
36
+ i0.ɵɵdomElementEnd()();
37
+ } if (rf & 2) {
38
+ i0.ɵɵclassProp("mj-page-search--focused", ctx.focused);
39
+ i0.ɵɵadvance();
40
+ i0.ɵɵclassMap(ctx.Icon);
41
+ i0.ɵɵadvance();
42
+ i0.ɵɵdomProperty("placeholder", ctx.Placeholder)("value", ctx.Value);
43
+ } }, styles: ["[_nghost-%COMP%] {\n display: inline-flex;\n min-width: 220px;\n }\n\n \n\n\n\n\n\n .mj-page-search[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2);\n width: 100%;\n padding: var(--mj-space-1) var(--mj-space-3);\n font-family: var(--mj-font-family);\n font-size: var(--mj-text-sm);\n line-height: 1.5;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n transition: var(--mj-transition-colors), box-shadow var(--mj-transition-base);\n min-height: 32px;\n box-sizing: border-box;\n }\n\n .mj-page-search[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-border-strong);\n }\n\n .mj-page-search--focused[_ngcontent-%COMP%] {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-focus-ring);\n }\n\n .mj-page-search[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 13px;\n flex-shrink: 0;\n }\n\n .mj-page-search[_ngcontent-%COMP%] input[_ngcontent-%COMP%] {\n flex: 1;\n border: none;\n outline: none;\n background: transparent;\n font: inherit;\n color: var(--mj-text-primary);\n min-width: 0;\n }\n\n .mj-page-search[_ngcontent-%COMP%] input[_ngcontent-%COMP%]::placeholder {\n color: var(--mj-text-disabled);\n }"] });
44
+ }
45
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJPageSearchComponent, [{
46
+ type: Component,
47
+ args: [{ selector: 'mj-page-search', standalone: true, template: `
48
+ <div class="mj-page-search" [class.mj-page-search--focused]="focused">
49
+ <i [class]="Icon"></i>
50
+ <input
51
+ type="text"
52
+ [placeholder]="Placeholder"
53
+ [value]="Value"
54
+ (input)="onInput($event)"
55
+ (focus)="focused = true"
56
+ (blur)="focused = false" />
57
+ </div>
58
+ `, styles: ["\n :host {\n display: inline-flex;\n min-width: 220px;\n }\n\n /* Compact toolbar density (32px) to match mjButton size=\"sm\" and other\n header widgets. White background + border-default for visual differentiation\n on both the page-header (white) and body (sunken) surfaces.\n Note: this is intentionally tighter than .mj-input (38px) because\n mj-page-search is a toolbar widget, not a form field. */\n .mj-page-search {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2);\n width: 100%;\n padding: var(--mj-space-1) var(--mj-space-3);\n font-family: var(--mj-font-family);\n font-size: var(--mj-text-sm);\n line-height: 1.5;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n transition: var(--mj-transition-colors), box-shadow var(--mj-transition-base);\n min-height: 32px;\n box-sizing: border-box;\n }\n\n .mj-page-search:hover {\n border-color: var(--mj-border-strong);\n }\n\n .mj-page-search--focused {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-focus-ring);\n }\n\n .mj-page-search i {\n color: var(--mj-text-muted);\n font-size: 13px;\n flex-shrink: 0;\n }\n\n .mj-page-search input {\n flex: 1;\n border: none;\n outline: none;\n background: transparent;\n font: inherit;\n color: var(--mj-text-primary);\n min-width: 0;\n }\n\n .mj-page-search input::placeholder {\n color: var(--mj-text-disabled);\n }\n "] }]
59
+ }], null, { Placeholder: [{
60
+ type: Input
61
+ }], Value: [{
62
+ type: Input
63
+ }], Icon: [{
64
+ type: Input
65
+ }], ValueChange: [{
66
+ type: Output
67
+ }] }); })();
68
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJPageSearchComponent, { className: "MJPageSearchComponent", filePath: "lib/page-search/page-search.component.ts", lineNumber: 93 }); })();
69
+ //# sourceMappingURL=page-search.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-search.component.js","sourceRoot":"","sources":["../../../src/lib/page-search/page-search.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;;AAEvE;;;;;;;;;;;;;;;GAeG;AA2EH,MAAM,OAAO,qBAAqB;IACvB,WAAW,GAAW,WAAW,CAAC;IAClC,KAAK,GAAW,EAAE,CAAC;IACnB,IAAI,GAAW,oBAAoB,CAAC;IACnC,WAAW,GAAG,IAAI,YAAY,EAAU,CAAC;IAE5C,OAAO,GAAY,KAAK,CAAC;IAEzB,OAAO,CAAC,KAAY;QACzB,MAAM,CAAC,GAAI,KAAK,CAAC,MAA2B,CAAC,KAAK,CAAC;QACnD,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;QACf,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC;+GAZU,qBAAqB;6DAArB,qBAAqB;YAtE9B,iCAAsE;YACpE,uBAAsB;YACtB,mCAM6B;YAA3B,AADA,AADA,0GAAS,mBAAe,IAAC,kGACN,IAAI,IAAC,gGACN,KAAK,IAAC;YAC5B,AAPE,oBAM6B,EACzB;;YATsB,sDAAyC;YAChE,cAAc;YAAd,uBAAc;YAGf,cAA2B;YAC3B,AADA,gDAA2B,oBACZ;;;iFAiEV,qBAAqB;cA1EjC,SAAS;2BACE,gBAAgB,cACd,IAAI,YACN;;;;;;;;;;;GAWT;;kBA6DA,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,MAAM;;kFAJI,qBAAqB","sourcesContent":["import { Component, EventEmitter, Input, Output } from '@angular/core';\n\n/**\n * mj-page-search — Canonical in-page search input for dashboard headers and toolbars.\n * Replaces per-page `.search-input-wrapper` rules so styling can't drift.\n *\n * Distinct from `mj-search-input` (the navbar/global search in `@memberjunction/ng-search`)\n * — this one is a simple text input used for filtering page content.\n *\n * Example:\n * ```html\n * <mj-page-search\n * Placeholder=\"Search templates...\"\n * [Value]=\"searchTerm\"\n * (ValueChange)=\"onSearch($event)\">\n * </mj-page-search>\n * ```\n */\n@Component({\n selector: 'mj-page-search',\n standalone: true,\n template: `\n <div class=\"mj-page-search\" [class.mj-page-search--focused]=\"focused\">\n <i [class]=\"Icon\"></i>\n <input\n type=\"text\"\n [placeholder]=\"Placeholder\"\n [value]=\"Value\"\n (input)=\"onInput($event)\"\n (focus)=\"focused = true\"\n (blur)=\"focused = false\" />\n </div>\n `,\n styles: [`\n :host {\n display: inline-flex;\n min-width: 220px;\n }\n\n /* Compact toolbar density (32px) to match mjButton size=\"sm\" and other\n header widgets. White background + border-default for visual differentiation\n on both the page-header (white) and body (sunken) surfaces.\n Note: this is intentionally tighter than .mj-input (38px) because\n mj-page-search is a toolbar widget, not a form field. */\n .mj-page-search {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2);\n width: 100%;\n padding: var(--mj-space-1) var(--mj-space-3);\n font-family: var(--mj-font-family);\n font-size: var(--mj-text-sm);\n line-height: 1.5;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n transition: var(--mj-transition-colors), box-shadow var(--mj-transition-base);\n min-height: 32px;\n box-sizing: border-box;\n }\n\n .mj-page-search:hover {\n border-color: var(--mj-border-strong);\n }\n\n .mj-page-search--focused {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-focus-ring);\n }\n\n .mj-page-search i {\n color: var(--mj-text-muted);\n font-size: 13px;\n flex-shrink: 0;\n }\n\n .mj-page-search input {\n flex: 1;\n border: none;\n outline: none;\n background: transparent;\n font: inherit;\n color: var(--mj-text-primary);\n min-width: 0;\n }\n\n .mj-page-search input::placeholder {\n color: var(--mj-text-disabled);\n }\n `]\n})\nexport class MJPageSearchComponent {\n @Input() Placeholder: string = 'Search...';\n @Input() Value: string = '';\n @Input() Icon: string = 'fa-solid fa-search';\n @Output() ValueChange = new EventEmitter<string>();\n\n public focused: boolean = false;\n\n public onInput(event: Event): void {\n const v = (event.target as HTMLInputElement).value;\n this.Value = v;\n this.ValueChange.emit(v);\n }\n}\n"]}
@@ -0,0 +1,46 @@
1
+ import { EventEmitter } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ /**
4
+ * mj-refresh-button — Canonical Refresh button for the `[actions]` slot of
5
+ * `<mj-page-header>`. Encapsulates the icon + spin-on-loading behavior so
6
+ * pages don't reinvent it every time.
7
+ *
8
+ * Replaces the repeated:
9
+ * ```html
10
+ * <button mjButton variant="secondary" size="sm" (click)="LoadData()" [disabled]="IsLoading" title="Refresh">
11
+ * <i class="fa-solid fa-arrows-rotate" [class.fa-spin]="IsLoading"></i> Refresh
12
+ * </button>
13
+ * ```
14
+ *
15
+ * Usage:
16
+ * ```html
17
+ * <mj-refresh-button [Loading]="IsLoading" (Clicked)="LoadData()" />
18
+ *
19
+ * <mj-refresh-button [Loading]="IsLoading" (Clicked)="Refresh()" [ShowLabel]="false" /> <!-- icon-only -->
20
+ * ```
21
+ *
22
+ * The `Loading` input drives both the spinner AND the disabled state — pass
23
+ * the page's `IsLoading` flag and you're done.
24
+ */
25
+ export declare class MJRefreshButtonComponent {
26
+ /** When true, the icon spins and the button is disabled. Pass `IsLoading` from the page. */
27
+ Loading: boolean;
28
+ /** Force-disable independently of `Loading`. */
29
+ Disabled: boolean;
30
+ /** Visible button text. Defaults to "Refresh". Use `[ShowLabel]="false"` for icon-only. */
31
+ Label: string;
32
+ /** Hover/aria tooltip. Defaults to "Refresh". */
33
+ Title: string;
34
+ /** Show the text label next to the icon. Defaults to true. */
35
+ ShowLabel: boolean;
36
+ /** Button variant passed to mjButton. Defaults to 'secondary'. */
37
+ Variant: 'primary' | 'secondary' | 'outline' | 'flat' | 'icon';
38
+ /** Button size passed to mjButton. Defaults to 'sm' to match page-header density. */
39
+ Size: 'sm' | 'md' | 'lg';
40
+ /** Emitted when the button is clicked AND the button isn't disabled/loading. */
41
+ Clicked: EventEmitter<MouseEvent>;
42
+ OnClick(event: MouseEvent): void;
43
+ static ɵfac: i0.ɵɵFactoryDeclaration<MJRefreshButtonComponent, never>;
44
+ static ɵcmp: i0.ɵɵComponentDeclaration<MJRefreshButtonComponent, "mj-refresh-button", never, { "Loading": { "alias": "Loading"; "required": false; }; "Disabled": { "alias": "Disabled"; "required": false; }; "Label": { "alias": "Label"; "required": false; }; "Title": { "alias": "Title"; "required": false; }; "ShowLabel": { "alias": "ShowLabel"; "required": false; }; "Variant": { "alias": "Variant"; "required": false; }; "Size": { "alias": "Size"; "required": false; }; }, { "Clicked": "Clicked"; }, never, never, true, never>;
45
+ }
46
+ //# sourceMappingURL=refresh-button.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-button.component.d.ts","sourceRoot":"","sources":["../../../src/lib/refresh-button/refresh-button.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAiB,MAAM,eAAe,CAAC;;AAGvE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAwBa,wBAAwB;IACnC,4FAA4F;IACnF,OAAO,EAAE,OAAO,CAAS;IAElC,gDAAgD;IACvC,QAAQ,EAAE,OAAO,CAAS;IAEnC,2FAA2F;IAClF,KAAK,EAAE,MAAM,CAAa;IAEnC,iDAAiD;IACxC,KAAK,EAAE,MAAM,CAAa;IAEnC,8DAA8D;IACrD,SAAS,EAAE,OAAO,CAAQ;IAEnC,kEAAkE;IACzD,OAAO,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAe;IAEtF,qFAAqF;IAC5E,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAQ;IAEzC,gFAAgF;IACtE,OAAO,2BAAkC;IAEnD,OAAO,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;yCAzBrB,wBAAwB;2CAAxB,wBAAwB;CA6BpC"}
@@ -0,0 +1,106 @@
1
+ import { Component, EventEmitter, Input, Output } from '@angular/core';
2
+ import { MJButtonDirective } from '../button/button.directive';
3
+ import * as i0 from "@angular/core";
4
+ function MJRefreshButtonComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
5
+ i0.ɵɵtext(0);
6
+ } if (rf & 2) {
7
+ const ctx_r0 = i0.ɵɵnextContext();
8
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.Label, " ");
9
+ } }
10
+ /**
11
+ * mj-refresh-button — Canonical Refresh button for the `[actions]` slot of
12
+ * `<mj-page-header>`. Encapsulates the icon + spin-on-loading behavior so
13
+ * pages don't reinvent it every time.
14
+ *
15
+ * Replaces the repeated:
16
+ * ```html
17
+ * <button mjButton variant="secondary" size="sm" (click)="LoadData()" [disabled]="IsLoading" title="Refresh">
18
+ * <i class="fa-solid fa-arrows-rotate" [class.fa-spin]="IsLoading"></i> Refresh
19
+ * </button>
20
+ * ```
21
+ *
22
+ * Usage:
23
+ * ```html
24
+ * <mj-refresh-button [Loading]="IsLoading" (Clicked)="LoadData()" />
25
+ *
26
+ * <mj-refresh-button [Loading]="IsLoading" (Clicked)="Refresh()" [ShowLabel]="false" /> <!-- icon-only -->
27
+ * ```
28
+ *
29
+ * The `Loading` input drives both the spinner AND the disabled state — pass
30
+ * the page's `IsLoading` flag and you're done.
31
+ */
32
+ export class MJRefreshButtonComponent {
33
+ /** When true, the icon spins and the button is disabled. Pass `IsLoading` from the page. */
34
+ Loading = false;
35
+ /** Force-disable independently of `Loading`. */
36
+ Disabled = false;
37
+ /** Visible button text. Defaults to "Refresh". Use `[ShowLabel]="false"` for icon-only. */
38
+ Label = 'Refresh';
39
+ /** Hover/aria tooltip. Defaults to "Refresh". */
40
+ Title = 'Refresh';
41
+ /** Show the text label next to the icon. Defaults to true. */
42
+ ShowLabel = true;
43
+ /** Button variant passed to mjButton. Defaults to 'secondary'. */
44
+ Variant = 'secondary';
45
+ /** Button size passed to mjButton. Defaults to 'sm' to match page-header density. */
46
+ Size = 'sm';
47
+ /** Emitted when the button is clicked AND the button isn't disabled/loading. */
48
+ Clicked = new EventEmitter();
49
+ OnClick(event) {
50
+ if (this.Disabled || this.Loading)
51
+ return;
52
+ this.Clicked.emit(event);
53
+ }
54
+ static ɵfac = function MJRefreshButtonComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MJRefreshButtonComponent)(); };
55
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJRefreshButtonComponent, selectors: [["mj-refresh-button"]], inputs: { Loading: "Loading", Disabled: "Disabled", Label: "Label", Title: "Title", ShowLabel: "ShowLabel", Variant: "Variant", Size: "Size" }, outputs: { Clicked: "Clicked" }, decls: 3, vars: 8, consts: [["type", "button", "mjButton", "", 3, "click", "variant", "size", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-arrows-rotate"]], template: function MJRefreshButtonComponent_Template(rf, ctx) { if (rf & 1) {
56
+ i0.ɵɵelementStart(0, "button", 0);
57
+ i0.ɵɵlistener("click", function MJRefreshButtonComponent_Template_button_click_0_listener($event) { return ctx.OnClick($event); });
58
+ i0.ɵɵelement(1, "i", 1);
59
+ i0.ɵɵconditionalCreate(2, MJRefreshButtonComponent_Conditional_2_Template, 1, 1);
60
+ i0.ɵɵelementEnd();
61
+ } if (rf & 2) {
62
+ i0.ɵɵproperty("variant", ctx.Variant)("size", ctx.Size)("disabled", ctx.Disabled || ctx.Loading);
63
+ i0.ɵɵattribute("title", ctx.Title)("aria-label", !ctx.ShowLabel ? ctx.Title : null);
64
+ i0.ɵɵadvance();
65
+ i0.ɵɵclassProp("fa-spin", ctx.Loading);
66
+ i0.ɵɵadvance();
67
+ i0.ɵɵconditional(ctx.ShowLabel ? 2 : -1);
68
+ } }, dependencies: [MJButtonDirective], styles: ["[_nghost-%COMP%] { display: inline-flex; }"] });
69
+ }
70
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJRefreshButtonComponent, [{
71
+ type: Component,
72
+ args: [{ selector: 'mj-refresh-button', standalone: true, imports: [MJButtonDirective], template: `
73
+ <button
74
+ type="button"
75
+ mjButton
76
+ [variant]="Variant"
77
+ [size]="Size"
78
+ [disabled]="Disabled || Loading"
79
+ [attr.title]="Title"
80
+ [attr.aria-label]="!ShowLabel ? Title : null"
81
+ (click)="OnClick($event)">
82
+ <i class="fa-solid fa-arrows-rotate" [class.fa-spin]="Loading" aria-hidden="true"></i>
83
+ @if (ShowLabel) {
84
+ {{ Label }}
85
+ }
86
+ </button>
87
+ `, styles: ["\n :host { display: inline-flex; }\n "] }]
88
+ }], null, { Loading: [{
89
+ type: Input
90
+ }], Disabled: [{
91
+ type: Input
92
+ }], Label: [{
93
+ type: Input
94
+ }], Title: [{
95
+ type: Input
96
+ }], ShowLabel: [{
97
+ type: Input
98
+ }], Variant: [{
99
+ type: Input
100
+ }], Size: [{
101
+ type: Input
102
+ }], Clicked: [{
103
+ type: Output
104
+ }] }); })();
105
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJRefreshButtonComponent, { className: "MJRefreshButtonComponent", filePath: "lib/refresh-button/refresh-button.component.ts", lineNumber: 50 }); })();
106
+ //# sourceMappingURL=refresh-button.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-button.component.js","sourceRoot":"","sources":["../../../src/lib/refresh-button/refresh-button.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;;;IAwCvD,YACF;;;IADE,6CACF;;AAvCN;;;;;;;;;;;;;;;;;;;;;GAqBG;AAyBH,MAAM,OAAO,wBAAwB;IACnC,4FAA4F;IACnF,OAAO,GAAY,KAAK,CAAC;IAElC,gDAAgD;IACvC,QAAQ,GAAY,KAAK,CAAC;IAEnC,2FAA2F;IAClF,KAAK,GAAW,SAAS,CAAC;IAEnC,iDAAiD;IACxC,KAAK,GAAW,SAAS,CAAC;IAEnC,8DAA8D;IACrD,SAAS,GAAY,IAAI,CAAC;IAEnC,kEAAkE;IACzD,OAAO,GAA0D,WAAW,CAAC;IAEtF,qFAAqF;IAC5E,IAAI,GAAuB,IAAI,CAAC;IAEzC,gFAAgF;IACtE,OAAO,GAAG,IAAI,YAAY,EAAc,CAAC;IAEnD,OAAO,CAAC,KAAiB;QACvB,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;kHA5BU,wBAAwB;6DAAxB,wBAAwB;YAnBjC,iCAQ4B;YAA1B,2GAAS,mBAAe,IAAC;YACzB,uBAAsF;YACtF,gFAAiB;YAGnB,iBAAS;;YARP,AADA,AADA,qCAAmB,kBACN,yCACmB;;YAIK,cAAyB;YAAzB,sCAAyB;YAC9D,cAEC;YAFD,wCAEC;4BAdK,iBAAiB;;iFAqBhB,wBAAwB;cAxBpC,SAAS;2BACE,mBAAmB,cACjB,IAAI,WACP,CAAC,iBAAiB,CAAC,YAClB;;;;;;;;;;;;;;;GAeT;;kBAOA,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAGL,MAAM;;kFAvBI,wBAAwB","sourcesContent":["import { Component, EventEmitter, Input, Output } from '@angular/core';\nimport { MJButtonDirective } from '../button/button.directive';\n\n/**\n * mj-refresh-button — Canonical Refresh button for the `[actions]` slot of\n * `<mj-page-header>`. Encapsulates the icon + spin-on-loading behavior so\n * pages don't reinvent it every time.\n *\n * Replaces the repeated:\n * ```html\n * <button mjButton variant=\"secondary\" size=\"sm\" (click)=\"LoadData()\" [disabled]=\"IsLoading\" title=\"Refresh\">\n * <i class=\"fa-solid fa-arrows-rotate\" [class.fa-spin]=\"IsLoading\"></i> Refresh\n * </button>\n * ```\n *\n * Usage:\n * ```html\n * <mj-refresh-button [Loading]=\"IsLoading\" (Clicked)=\"LoadData()\" />\n *\n * <mj-refresh-button [Loading]=\"IsLoading\" (Clicked)=\"Refresh()\" [ShowLabel]=\"false\" /> <!-- icon-only -->\n * ```\n *\n * The `Loading` input drives both the spinner AND the disabled state — pass\n * the page's `IsLoading` flag and you're done.\n */\n@Component({\n selector: 'mj-refresh-button',\n standalone: true,\n imports: [MJButtonDirective],\n template: `\n <button\n type=\"button\"\n mjButton\n [variant]=\"Variant\"\n [size]=\"Size\"\n [disabled]=\"Disabled || Loading\"\n [attr.title]=\"Title\"\n [attr.aria-label]=\"!ShowLabel ? Title : null\"\n (click)=\"OnClick($event)\">\n <i class=\"fa-solid fa-arrows-rotate\" [class.fa-spin]=\"Loading\" aria-hidden=\"true\"></i>\n @if (ShowLabel) {\n {{ Label }}\n }\n </button>\n `,\n styles: [`\n :host { display: inline-flex; }\n `]\n})\nexport class MJRefreshButtonComponent {\n /** When true, the icon spins and the button is disabled. Pass `IsLoading` from the page. */\n @Input() Loading: boolean = false;\n\n /** Force-disable independently of `Loading`. */\n @Input() Disabled: boolean = false;\n\n /** Visible button text. Defaults to \"Refresh\". Use `[ShowLabel]=\"false\"` for icon-only. */\n @Input() Label: string = 'Refresh';\n\n /** Hover/aria tooltip. Defaults to \"Refresh\". */\n @Input() Title: string = 'Refresh';\n\n /** Show the text label next to the icon. Defaults to true. */\n @Input() ShowLabel: boolean = true;\n\n /** Button variant passed to mjButton. Defaults to 'secondary'. */\n @Input() Variant: 'primary' | 'secondary' | 'outline' | 'flat' | 'icon' = 'secondary';\n\n /** Button size passed to mjButton. Defaults to 'sm' to match page-header density. */\n @Input() Size: 'sm' | 'md' | 'lg' = 'sm';\n\n /** Emitted when the button is clicked AND the button isn't disabled/loading. */\n @Output() Clicked = new EventEmitter<MouseEvent>();\n\n OnClick(event: MouseEvent): void {\n if (this.Disabled || this.Loading) return;\n this.Clicked.emit(event);\n }\n}\n"]}