@memberjunction/ng-base-forms 6.1.0-edge.5 → 6.1.0-edge.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/base-form-component.d.ts +23 -0
- package/dist/lib/base-form-component.d.ts.map +1 -1
- package/dist/lib/base-form-component.js +56 -13
- package/dist/lib/base-form-component.js.map +1 -1
- package/dist/lib/chrome/form-chrome-rail-pref.d.ts +22 -0
- package/dist/lib/chrome/form-chrome-rail-pref.d.ts.map +1 -1
- package/dist/lib/chrome/form-chrome-rail-pref.js +29 -0
- package/dist/lib/chrome/form-chrome-rail-pref.js.map +1 -1
- package/dist/lib/chrome/form-chrome.d.ts +19 -0
- package/dist/lib/chrome/form-chrome.d.ts.map +1 -1
- package/dist/lib/chrome/form-chrome.js +26 -0
- package/dist/lib/chrome/form-chrome.js.map +1 -1
- package/dist/lib/container/record-form-container.component.d.ts +52 -2
- package/dist/lib/container/record-form-container.component.d.ts.map +1 -1
- package/dist/lib/container/record-form-container.component.js +485 -161
- package/dist/lib/container/record-form-container.component.js.map +1 -1
- package/dist/lib/explorer-entity-data-grid.component.d.ts +36 -0
- package/dist/lib/explorer-entity-data-grid.component.d.ts.map +1 -1
- package/dist/lib/explorer-entity-data-grid.component.js +112 -3
- package/dist/lib/explorer-entity-data-grid.component.js.map +1 -1
- package/dist/lib/field/form-field.component.d.ts +128 -0
- package/dist/lib/field/form-field.component.d.ts.map +1 -1
- package/dist/lib/field/form-field.component.js +871 -306
- package/dist/lib/field/form-field.component.js.map +1 -1
- package/dist/lib/isa-related-panel/isa-related-card.component.d.ts.map +1 -1
- package/dist/lib/isa-related-panel/isa-related-card.component.js +7 -1
- package/dist/lib/isa-related-panel/isa-related-card.component.js.map +1 -1
- package/dist/lib/overlays/base-form-overlay.d.ts.map +1 -1
- package/dist/lib/overlays/base-form-overlay.js +3 -1
- package/dist/lib/overlays/base-form-overlay.js.map +1 -1
- package/dist/lib/overlays/form-slide-in.component.js +10 -11
- package/dist/lib/overlays/form-slide-in.component.js.map +1 -1
- package/dist/lib/panel/collapsible-panel.component.d.ts +94 -3
- package/dist/lib/panel/collapsible-panel.component.d.ts.map +1 -1
- package/dist/lib/panel/collapsible-panel.component.js +278 -37
- package/dist/lib/panel/collapsible-panel.component.js.map +1 -1
- package/dist/lib/panel-slot/base-form-panel.d.ts +12 -0
- package/dist/lib/panel-slot/base-form-panel.d.ts.map +1 -1
- package/dist/lib/panel-slot/base-form-panel.js.map +1 -1
- package/dist/lib/related-grid-height.d.ts +48 -11
- package/dist/lib/related-grid-height.d.ts.map +1 -1
- package/dist/lib/related-grid-height.js +53 -12
- package/dist/lib/related-grid-height.js.map +1 -1
- package/dist/lib/section-indicators/form-section-indicator-coordinator.service.d.ts +80 -0
- package/dist/lib/section-indicators/form-section-indicator-coordinator.service.d.ts.map +1 -0
- package/dist/lib/section-indicators/form-section-indicator-coordinator.service.js +99 -0
- package/dist/lib/section-indicators/form-section-indicator-coordinator.service.js.map +1 -0
- package/dist/lib/section-indicators/form-section-indicators.d.ts +97 -0
- package/dist/lib/section-indicators/form-section-indicators.d.ts.map +1 -0
- package/dist/lib/section-indicators/form-section-indicators.js +132 -0
- package/dist/lib/section-indicators/form-section-indicators.js.map +1 -0
- package/dist/lib/section-manager/section-manager.component.js +2 -2
- package/dist/lib/section-manager/section-manager.component.js.map +1 -1
- package/dist/lib/toolbar/form-toolbar.component.d.ts +9 -1
- package/dist/lib/toolbar/form-toolbar.component.d.ts.map +1 -1
- package/dist/lib/toolbar/form-toolbar.component.js +14 -6
- package/dist/lib/toolbar/form-toolbar.component.js.map +1 -1
- package/dist/lib/types/form-types.d.ts +8 -0
- package/dist/lib/types/form-types.d.ts.map +1 -1
- package/dist/lib/types/form-types.js.map +1 -1
- package/dist/public-api.d.ts +2 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +3 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +25 -27
|
@@ -6,47 +6,80 @@ import { MjFormFieldComponent } from '../field/form-field.component';
|
|
|
6
6
|
import { CompositeKey } from '@memberjunction/core';
|
|
7
7
|
import { EscapeHTML, HighlightSearchMatches } from '@memberjunction/global';
|
|
8
8
|
import { FormChromeCoordinator } from '../chrome/form-chrome-coordinator.service';
|
|
9
|
+
import { FormSectionIndicatorCoordinator } from '../section-indicators/form-section-indicator-coordinator.service';
|
|
10
|
+
import { ClaimedCollectionErrors, DescribeSectionDirty, DescribeSectionErrors, DescribeSectionWarnings, SectionOwnsValidationSource, SumSectionIndicators, TallyValidationErrors, } from '../section-indicators/form-section-indicators';
|
|
9
11
|
import * as i0 from "@angular/core";
|
|
10
12
|
const _c0 = ["panelContent"];
|
|
11
13
|
const _c1 = ["*"];
|
|
12
14
|
function MjCollapsiblePanelComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
13
|
-
const
|
|
14
|
-
i0.ɵɵelementStart(0, "div",
|
|
15
|
-
i0.ɵɵlistener("dragstart", function MjCollapsiblePanelComponent_Conditional_2_Template_div_dragstart_0_listener($event) { i0.ɵɵrestoreView(
|
|
16
|
-
i0.ɵɵelement(1, "i",
|
|
15
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
16
|
+
i0.ɵɵelementStart(0, "div", 16);
|
|
17
|
+
i0.ɵɵlistener("dragstart", function MjCollapsiblePanelComponent_Conditional_2_Template_div_dragstart_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnDragStart($event)); })("dragend", function MjCollapsiblePanelComponent_Conditional_2_Template_div_dragend_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnDragEnd()); })("click", function MjCollapsiblePanelComponent_Conditional_2_Template_div_click_0_listener($event) { return $event.stopPropagation(); });
|
|
18
|
+
i0.ɵɵelement(1, "i", 17);
|
|
17
19
|
i0.ɵɵelementEnd();
|
|
18
20
|
} }
|
|
19
21
|
function MjCollapsiblePanelComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
20
|
-
i0.ɵɵ
|
|
22
|
+
i0.ɵɵelement(0, "span", 8);
|
|
23
|
+
} if (rf & 2) {
|
|
24
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
25
|
+
i0.ɵɵproperty("title", ctx_r1.SectionDirtyTitle);
|
|
26
|
+
i0.ɵɵattribute("aria-label", ctx_r1.SectionDirtyTitle);
|
|
27
|
+
} }
|
|
28
|
+
function MjCollapsiblePanelComponent_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
29
|
+
i0.ɵɵelementStart(0, "span", 9);
|
|
30
|
+
i0.ɵɵelement(1, "i", 18);
|
|
31
|
+
i0.ɵɵtext(2);
|
|
32
|
+
i0.ɵɵelementEnd();
|
|
33
|
+
} if (rf & 2) {
|
|
34
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
35
|
+
i0.ɵɵproperty("title", ctx_r1.SectionErrorTitle);
|
|
36
|
+
i0.ɵɵattribute("aria-label", ctx_r1.SectionErrorTitle);
|
|
37
|
+
i0.ɵɵadvance(2);
|
|
38
|
+
i0.ɵɵtextInterpolate1("", ctx_r1.SectionErrorCount, " ");
|
|
39
|
+
} }
|
|
40
|
+
function MjCollapsiblePanelComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
41
|
+
i0.ɵɵelementStart(0, "span", 10);
|
|
42
|
+
i0.ɵɵelement(1, "i", 19);
|
|
43
|
+
i0.ɵɵtext(2);
|
|
44
|
+
i0.ɵɵelementEnd();
|
|
45
|
+
} if (rf & 2) {
|
|
46
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
47
|
+
i0.ɵɵproperty("title", ctx_r1.SectionWarningTitle);
|
|
48
|
+
i0.ɵɵattribute("aria-label", ctx_r1.SectionWarningTitle);
|
|
49
|
+
i0.ɵɵadvance(2);
|
|
50
|
+
i0.ɵɵtextInterpolate1("", ctx_r1.SectionWarningCount, " ");
|
|
51
|
+
} }
|
|
52
|
+
function MjCollapsiblePanelComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
53
|
+
i0.ɵɵelementStart(0, "span", 20);
|
|
21
54
|
i0.ɵɵtext(1);
|
|
22
55
|
i0.ɵɵelementEnd();
|
|
23
56
|
} if (rf & 2) {
|
|
24
|
-
const
|
|
25
|
-
i0.ɵɵclassProp("mj-forms-row-count-badge--zero",
|
|
57
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
58
|
+
i0.ɵɵclassProp("mj-forms-row-count-badge--zero", ctx_r1.BadgeCount === 0);
|
|
26
59
|
i0.ɵɵadvance();
|
|
27
|
-
i0.ɵɵtextInterpolate1(" ",
|
|
60
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.BadgeCount, " ");
|
|
28
61
|
} }
|
|
29
|
-
function
|
|
30
|
-
const
|
|
31
|
-
i0.ɵɵelementStart(0, "a",
|
|
32
|
-
i0.ɵɵlistener("click", function
|
|
33
|
-
i0.ɵɵelement(1, "i",
|
|
62
|
+
function MjCollapsiblePanelComponent_Conditional_11_Template(rf, ctx) { if (rf & 1) {
|
|
63
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
64
|
+
i0.ɵɵelementStart(0, "a", 21);
|
|
65
|
+
i0.ɵɵlistener("click", function MjCollapsiblePanelComponent_Conditional_11_Template_a_click_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnInheritedBadgeClick($event)); });
|
|
66
|
+
i0.ɵɵelement(1, "i", 22);
|
|
34
67
|
i0.ɵɵtext(2);
|
|
35
68
|
i0.ɵɵelementEnd();
|
|
36
69
|
} if (rf & 2) {
|
|
37
|
-
const
|
|
38
|
-
i0.ɵɵproperty("title", "View parent record: " +
|
|
70
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
71
|
+
i0.ɵɵproperty("title", "View parent record: " + ctx_r1.InheritedFromEntity);
|
|
39
72
|
i0.ɵɵadvance(2);
|
|
40
|
-
i0.ɵɵtextInterpolate1(" Inherited from ",
|
|
73
|
+
i0.ɵɵtextInterpolate1(" Inherited from ", ctx_r1.InheritedFromEntity, " ");
|
|
41
74
|
} }
|
|
42
|
-
function
|
|
43
|
-
i0.ɵɵelementStart(0, "div",
|
|
75
|
+
function MjCollapsiblePanelComponent_Conditional_12_Template(rf, ctx) { if (rf & 1) {
|
|
76
|
+
i0.ɵɵelementStart(0, "div", 13);
|
|
44
77
|
i0.ɵɵelement(1, "i");
|
|
45
78
|
i0.ɵɵelementEnd();
|
|
46
79
|
} if (rf & 2) {
|
|
47
|
-
const
|
|
80
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
48
81
|
i0.ɵɵadvance();
|
|
49
|
-
i0.ɵɵclassMap(
|
|
82
|
+
i0.ɵɵclassMap(ctx_r1.Expanded ? "fa fa-chevron-up" : "fa fa-chevron-down");
|
|
50
83
|
} }
|
|
51
84
|
/**
|
|
52
85
|
* Reusable collapsible panel for form sections.
|
|
@@ -62,6 +95,10 @@ function MjCollapsiblePanelComponent_Conditional_9_Template(rf, ctx) { if (rf &
|
|
|
62
95
|
* - Drag-to-reorder sections
|
|
63
96
|
* - Inheritable "Inherited from X" badge with navigation event
|
|
64
97
|
* - Row count badge for related entity sections
|
|
98
|
+
* - Section indicators: an unsaved-changes dot and an invalid-field count derived
|
|
99
|
+
* live from the projected `mj-form-field`s (plus anything a custom section
|
|
100
|
+
* supplies through `[Indicators]`), published to the form's chrome rail
|
|
101
|
+
* through {@link FormSectionIndicatorCoordinator}
|
|
65
102
|
*
|
|
66
103
|
* @example
|
|
67
104
|
* ```html
|
|
@@ -83,8 +120,33 @@ export class MjCollapsiblePanelComponent {
|
|
|
83
120
|
elementRef = inject(ElementRef);
|
|
84
121
|
ngZone = inject(NgZone);
|
|
85
122
|
chrome = inject(FormChromeCoordinator, { optional: true });
|
|
123
|
+
indicators = inject(FormSectionIndicatorCoordinator, { optional: true });
|
|
86
124
|
/** Unique key for state persistence */
|
|
87
125
|
SectionKey = '';
|
|
126
|
+
/**
|
|
127
|
+
* Indicator counts a custom section supplies for content that is NOT rendered by
|
|
128
|
+
* `mj-form-field` — a designer, an inline grid, a hand-built editor. Added to the
|
|
129
|
+
* counts this panel derives from its own fields, so a section that mixes both
|
|
130
|
+
* reports the union. Omit for ordinary field panels: their state is derived.
|
|
131
|
+
*
|
|
132
|
+
* ```html
|
|
133
|
+
* <mj-collapsible-panel SectionKey="lines" [Indicators]="{ DirtyCount: LineEditor.EditedRows, ErrorCount: LineEditor.InvalidRows }">
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
Indicators;
|
|
137
|
+
/**
|
|
138
|
+
* Graph-path collection names whose validation failures belong to THIS panel —
|
|
139
|
+
* `ValidationSources="Modifications"` claims `Modifications[2].ProvisionID`.
|
|
140
|
+
*
|
|
141
|
+
* Only needed when a panel hosts a child collection whose path name differs from
|
|
142
|
+
* its `SectionKey`: plain field failures are joined automatically from the panel's
|
|
143
|
+
* `mj-form-field` children, and a graph path whose leading segment already matches
|
|
144
|
+
* the `SectionKey` is claimed without any declaration. Declared HERE, on the panel
|
|
145
|
+
* that owns the collection, rather than in an app-side `fieldName -> sectionKey`
|
|
146
|
+
* map that duplicates a fact the panel owns and drifts silently when a
|
|
147
|
+
* `SectionKey` is renamed. Accepts a single name or a list.
|
|
148
|
+
*/
|
|
149
|
+
ValidationSources;
|
|
88
150
|
/** Display name shown in the panel header */
|
|
89
151
|
SectionName = '';
|
|
90
152
|
/** Font Awesome icon class for the panel header */
|
|
@@ -163,6 +225,16 @@ export class MjCollapsiblePanelComponent {
|
|
|
163
225
|
get HostIcon() {
|
|
164
226
|
return this.Icon;
|
|
165
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* Indicator counts mirrored onto the host element, so CSS and any DOM sweep can
|
|
230
|
+
* read a section's state without a component reference.
|
|
231
|
+
*/
|
|
232
|
+
get HostDirtyCount() {
|
|
233
|
+
return this.SectionIndicators.DirtyCount;
|
|
234
|
+
}
|
|
235
|
+
get HostErrorCount() {
|
|
236
|
+
return this.SectionIndicators.ErrorCount;
|
|
237
|
+
}
|
|
166
238
|
get HostClass() {
|
|
167
239
|
const classes = [`mj-panel--${this.Variant}`];
|
|
168
240
|
if (!this.IsVisible)
|
|
@@ -171,6 +243,13 @@ export class MjCollapsiblePanelComponent {
|
|
|
171
243
|
classes.push('mj-dragging');
|
|
172
244
|
if (this.IsDragOver)
|
|
173
245
|
classes.push('mj-drag-over');
|
|
246
|
+
const indicators = this.SectionIndicators;
|
|
247
|
+
if (indicators.DirtyCount > 0)
|
|
248
|
+
classes.push('mj-panel-dirty');
|
|
249
|
+
if (indicators.ErrorCount > 0)
|
|
250
|
+
classes.push('mj-panel-has-errors');
|
|
251
|
+
else if (indicators.WarningCount > 0)
|
|
252
|
+
classes.push('mj-panel-has-warnings');
|
|
174
253
|
if (this.chrome?.Spec.RelatedRoles.get(this.SectionKey) === 'Detail') {
|
|
175
254
|
classes.push('mj-form-role-detail');
|
|
176
255
|
}
|
|
@@ -245,12 +324,121 @@ export class MjCollapsiblePanelComponent {
|
|
|
245
324
|
const formRef = this.Form;
|
|
246
325
|
return formRef?.IsSectionExpanded ? formRef.IsSectionExpanded(this.SectionKey, this.DefaultExpanded) : true;
|
|
247
326
|
}
|
|
327
|
+
// ---- Section indicators (unsaved-changes dot + invalid-field count) ----
|
|
328
|
+
/**
|
|
329
|
+
* Live state of this section: fields edited since the last save, fields that are
|
|
330
|
+
* invalid or required-and-empty, and fields carrying warnings. Derived from the
|
|
331
|
+
* projected `mj-form-field`s on every read — the same state those fields use for
|
|
332
|
+
* their own amber dot and red underline, so the section can never disagree with
|
|
333
|
+
* its fields — plus graph-path validation failures this section claims, plus
|
|
334
|
+
* whatever a custom section supplies through `[Indicators]`.
|
|
335
|
+
*
|
|
336
|
+
* Read during change detection (host bindings, the header template, the rail via
|
|
337
|
+
* the coordinator). Pure and cheap: one pass over the field list.
|
|
338
|
+
*/
|
|
339
|
+
get SectionIndicators() {
|
|
340
|
+
return SumSectionIndicators(this.fieldIndicators(), TallyValidationErrors(this.claimedGraphErrors()), this.Indicators);
|
|
341
|
+
}
|
|
342
|
+
get SectionDirtyCount() {
|
|
343
|
+
return this.SectionIndicators.DirtyCount;
|
|
344
|
+
}
|
|
345
|
+
get SectionErrorCount() {
|
|
346
|
+
return this.SectionIndicators.ErrorCount;
|
|
347
|
+
}
|
|
348
|
+
get SectionWarningCount() {
|
|
349
|
+
return this.SectionIndicators.WarningCount;
|
|
350
|
+
}
|
|
351
|
+
get SectionDirtyTitle() {
|
|
352
|
+
return DescribeSectionDirty(this.SectionDirtyCount);
|
|
353
|
+
}
|
|
354
|
+
get SectionErrorTitle() {
|
|
355
|
+
return DescribeSectionErrors(this.SectionErrorCount);
|
|
356
|
+
}
|
|
357
|
+
get SectionWarningTitle() {
|
|
358
|
+
return DescribeSectionWarnings(this.SectionWarningCount);
|
|
359
|
+
}
|
|
360
|
+
/** {@link FormSectionIndicatorSource} — the rail reads this through the coordinator. */
|
|
361
|
+
GetSectionIndicators() {
|
|
362
|
+
return this.SectionIndicators;
|
|
363
|
+
}
|
|
364
|
+
/** {@link FormSectionIndicatorSource} — whether a form-level error belongs to this section. */
|
|
365
|
+
OwnsValidationSource(source) {
|
|
366
|
+
return SectionOwnsValidationSource(source, this.SectionKey, this.validationScope());
|
|
367
|
+
}
|
|
368
|
+
/** Normalized {@link ValidationSources}. */
|
|
369
|
+
get ValidationCollectionNames() {
|
|
370
|
+
const raw = this.ValidationSources;
|
|
371
|
+
if (!raw)
|
|
372
|
+
return [];
|
|
373
|
+
return (typeof raw === 'string' ? [raw] : [...raw]).map((n) => n.trim()).filter((n) => n.length > 0);
|
|
374
|
+
}
|
|
375
|
+
/** Field names this panel renders — the join between a plain-field error and this section. */
|
|
376
|
+
renderedFieldNames() {
|
|
377
|
+
const names = [];
|
|
378
|
+
this.FieldComponents?.forEach((field) => {
|
|
379
|
+
if (field.FieldName)
|
|
380
|
+
names.push(field.FieldName);
|
|
381
|
+
});
|
|
382
|
+
return names;
|
|
383
|
+
}
|
|
384
|
+
validationScope() {
|
|
385
|
+
return { FieldNames: this.renderedFieldNames(), CollectionNames: this.ValidationCollectionNames };
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Counts of FIELDS (not messages) in each state. A field is invalid when it shows a
|
|
389
|
+
* failure OR is required and empty in edit mode — exactly the two conditions that
|
|
390
|
+
* paint its underline red. Warnings count only on fields with no failure, mirroring
|
|
391
|
+
* `MjFormFieldComponent.ShowWarnings`.
|
|
392
|
+
*/
|
|
393
|
+
fieldIndicators() {
|
|
394
|
+
let DirtyCount = 0;
|
|
395
|
+
let ErrorCount = 0;
|
|
396
|
+
let WarningCount = 0;
|
|
397
|
+
this.FieldComponents?.forEach((field) => {
|
|
398
|
+
if (field.IsDirty)
|
|
399
|
+
DirtyCount++;
|
|
400
|
+
// Only a field that renders an EDITOR can be invalid: a read-only field renders its
|
|
401
|
+
// value as text and never paints an underline, so it is skipped even when
|
|
402
|
+
// `IsRequiredEmpty` would say otherwise (a NOT NULL `__mj_CreatedAt` on a new record
|
|
403
|
+
// is empty and read-only — the user cannot fix it, and it fills itself on save).
|
|
404
|
+
if (!field.EditMode || field.IsFieldReadOnly)
|
|
405
|
+
return;
|
|
406
|
+
if (field.ShowErrors || field.IsRequiredEmpty)
|
|
407
|
+
ErrorCount++;
|
|
408
|
+
// The field paints its amber underline on EITHER of these — a stored date the input
|
|
409
|
+
// cannot parse warns on its own, outside the validation pipeline.
|
|
410
|
+
else if (field.ShowWarnings || field.StoredDateIsUnreadable)
|
|
411
|
+
WarningCount++;
|
|
412
|
+
});
|
|
413
|
+
return { DirtyCount, ErrorCount, WarningCount };
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Graph-path failures (`Lines[2].Amount`) this section owns, once the form has asked
|
|
417
|
+
* for validation to show. Plain field-name failures are NOT counted here — the field
|
|
418
|
+
* that renders them already reports through {@link fieldIndicators}.
|
|
419
|
+
*/
|
|
420
|
+
claimedGraphErrors() {
|
|
421
|
+
const ctx = this.FormContext;
|
|
422
|
+
if (!ctx?.showValidation || !ctx.validationErrors?.length)
|
|
423
|
+
return [];
|
|
424
|
+
return ClaimedCollectionErrors(ctx.validationErrors, this.SectionKey, this.validationScope());
|
|
425
|
+
}
|
|
426
|
+
/** (Re)declare this section with the container-scoped registry the rail reads. */
|
|
427
|
+
registerIndicatorSource(previousKey) {
|
|
428
|
+
if (!this.indicators)
|
|
429
|
+
return;
|
|
430
|
+
if (previousKey && previousKey !== this.SectionKey) {
|
|
431
|
+
this.indicators.Unregister(this, previousKey);
|
|
432
|
+
}
|
|
433
|
+
this.indicators.Register(this);
|
|
434
|
+
}
|
|
248
435
|
// ---- Lifecycle ----
|
|
249
436
|
ngOnInit() {
|
|
250
437
|
this.DisplayName = this.SectionName;
|
|
251
438
|
this.chrome?.Changes.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
|
252
439
|
this.UpdateVisibilityAndHighlighting();
|
|
253
440
|
});
|
|
441
|
+
this.registerIndicatorSource();
|
|
254
442
|
}
|
|
255
443
|
ngAfterContentInit() {
|
|
256
444
|
this.UpdateFieldNames();
|
|
@@ -258,12 +446,18 @@ export class MjCollapsiblePanelComponent {
|
|
|
258
446
|
this.FieldComponents.changes.subscribe(() => {
|
|
259
447
|
this.UpdateFieldNames();
|
|
260
448
|
this.SubscribeToFieldNavigateEvents();
|
|
449
|
+
// The set of fields changed, so the section's counts (and which errors it
|
|
450
|
+
// claims) may have too — let the rail re-read.
|
|
451
|
+
this.indicators?.NotifyChanged();
|
|
261
452
|
});
|
|
262
453
|
}
|
|
263
454
|
ngOnChanges(changes) {
|
|
264
455
|
if (changes['SectionName']) {
|
|
265
456
|
this.DisplayName = this.SectionName;
|
|
266
457
|
}
|
|
458
|
+
if (changes['SectionKey'] && !changes['SectionKey'].firstChange) {
|
|
459
|
+
this.registerIndicatorSource(changes['SectionKey'].previousValue);
|
|
460
|
+
}
|
|
267
461
|
if (changes['SectionName'] || changes['FormContext']) {
|
|
268
462
|
this.UpdateVisibilityAndHighlighting();
|
|
269
463
|
}
|
|
@@ -277,6 +471,7 @@ export class MjCollapsiblePanelComponent {
|
|
|
277
471
|
this.SetupResizeObserver();
|
|
278
472
|
}
|
|
279
473
|
ngOnDestroy() {
|
|
474
|
+
this.indicators?.Unregister(this);
|
|
280
475
|
this.fieldNavReset$.next();
|
|
281
476
|
this.fieldNavReset$.complete();
|
|
282
477
|
this.destroy$.next();
|
|
@@ -425,7 +620,9 @@ export class MjCollapsiblePanelComponent {
|
|
|
425
620
|
}
|
|
426
621
|
/**
|
|
427
622
|
* Subscribes to Navigate events from all child form-field components
|
|
428
|
-
* and relays them through this panel's Navigate output.
|
|
623
|
+
* and relays them through this panel's Navigate output. Also listens for
|
|
624
|
+
* value edits so the section indicators (and the rail reading them) refresh
|
|
625
|
+
* on the same tick as the keystroke rather than on the container's next poll.
|
|
429
626
|
*/
|
|
430
627
|
SubscribeToFieldNavigateEvents() {
|
|
431
628
|
this.fieldNavReset$.next(); // tear down previous subscriptions
|
|
@@ -433,6 +630,10 @@ export class MjCollapsiblePanelComponent {
|
|
|
433
630
|
field.Navigate.pipe(takeUntil(this.fieldNavReset$)).subscribe((event) => {
|
|
434
631
|
this.Navigate.emit(event);
|
|
435
632
|
});
|
|
633
|
+
field.ValueChange.pipe(takeUntil(this.fieldNavReset$)).subscribe(() => {
|
|
634
|
+
this.cdr.markForCheck();
|
|
635
|
+
this.indicators?.NotifyChanged();
|
|
636
|
+
});
|
|
436
637
|
});
|
|
437
638
|
}
|
|
438
639
|
/**
|
|
@@ -484,6 +685,22 @@ export class MjCollapsiblePanelComponent {
|
|
|
484
685
|
formRef?.SetSectionPanelHeight?.(this.SectionKey, height);
|
|
485
686
|
}, 500);
|
|
486
687
|
}
|
|
688
|
+
/**
|
|
689
|
+
* True when this panel projects form fields and field-level security denies the user EVERY one
|
|
690
|
+
* of them.
|
|
691
|
+
*
|
|
692
|
+
* Deliberately false when the panel projects no `mj-form-field` at all — related-entity grids,
|
|
693
|
+
* IS-A cards and slot-injected panels all legitimately have none, and hiding those would remove
|
|
694
|
+
* sections field security has nothing to say about. "No fields" and "no readable fields" are
|
|
695
|
+
* different states and only the second one should hide the section.
|
|
696
|
+
*/
|
|
697
|
+
get AllProjectedFieldsDenied() {
|
|
698
|
+
const fields = this.FieldComponents;
|
|
699
|
+
if (!fields || fields.length === 0) {
|
|
700
|
+
return false;
|
|
701
|
+
}
|
|
702
|
+
return fields.toArray().every(f => !f.IsFieldReadableByUser);
|
|
703
|
+
}
|
|
487
704
|
UpdateVisibilityAndHighlighting() {
|
|
488
705
|
// Hard hide takes precedence over search state. Driven by an explicit
|
|
489
706
|
// `Hidden` input OR the form config's section-visibility rules carried on
|
|
@@ -495,6 +712,15 @@ export class MjCollapsiblePanelComponent {
|
|
|
495
712
|
this.cdr.markForCheck();
|
|
496
713
|
return;
|
|
497
714
|
}
|
|
715
|
+
// A section whose every field is denied by field-level security renders as a heading over
|
|
716
|
+
// nothing — the fields hide themselves individually, leaving an empty card that reads like a
|
|
717
|
+
// broken screen rather than a permissions boundary. Hide the section instead.
|
|
718
|
+
if (this.AllProjectedFieldsDenied) {
|
|
719
|
+
this.IsVisible = false;
|
|
720
|
+
this.DisplayName = EscapeHTML(this.SectionName);
|
|
721
|
+
this.cdr.markForCheck();
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
498
724
|
const searchTerm = (this.FormContext?.sectionFilter || '').toLowerCase().trim();
|
|
499
725
|
if (!searchTerm) {
|
|
500
726
|
this.IsVisible = this.hasRenderableContent();
|
|
@@ -523,29 +749,30 @@ export class MjCollapsiblePanelComponent {
|
|
|
523
749
|
} if (rf & 2) {
|
|
524
750
|
let _t;
|
|
525
751
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.panelContentRef = _t.first);
|
|
526
|
-
} }, hostVars:
|
|
752
|
+
} }, hostVars: 9, hostBindings: function MjCollapsiblePanelComponent_HostBindings(rf, ctx) { if (rf & 1) {
|
|
527
753
|
i0.ɵɵlistener("dragover", function MjCollapsiblePanelComponent_dragover_HostBindingHandler($event) { return ctx.OnDragOver($event); })("dragleave", function MjCollapsiblePanelComponent_dragleave_HostBindingHandler($event) { return ctx.OnDragLeave($event); })("drop", function MjCollapsiblePanelComponent_drop_HostBindingHandler($event) { return ctx.OnDrop($event); });
|
|
528
754
|
} if (rf & 2) {
|
|
529
|
-
i0.ɵɵattribute("data-section-key", ctx.HostSectionKey)("data-variant", ctx.HostVariant)("data-icon", ctx.HostIcon);
|
|
755
|
+
i0.ɵɵattribute("data-section-key", ctx.HostSectionKey)("data-variant", ctx.HostVariant)("data-icon", ctx.HostIcon)("data-dirty-count", ctx.HostDirtyCount)("data-error-count", ctx.HostErrorCount);
|
|
530
756
|
i0.ɵɵclassMap(ctx.HostClass);
|
|
531
757
|
i0.ɵɵstyleProp("order", ctx.CssOrder);
|
|
532
|
-
} }, inputs: { SectionKey: "SectionKey", SectionName: "SectionName", Icon: "Icon", Form: "Form", FormContext: "FormContext", Variant: "Variant", BadgeCount: "BadgeCount", DefaultExpanded: "DefaultExpanded", InheritedFromEntity: "InheritedFromEntity", InheritedRecordPrimaryKey: "InheritedRecordPrimaryKey", Hidden: "Hidden", _deprecatedSectionKey: [0, "sectionKey", "_deprecatedSectionKey"], _deprecatedSectionName: [0, "sectionName", "_deprecatedSectionName"], _deprecatedIcon: [0, "icon", "_deprecatedIcon"], _deprecatedForm: [0, "form", "_deprecatedForm"], _deprecatedFormContext: [0, "formContext", "_deprecatedFormContext"] }, outputs: { DragStarted: "DragStarted", DragEnded: "DragEnded", PanelDrop: "PanelDrop", Navigate: "Navigate" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], ngContentSelectors: _c1, decls:
|
|
533
|
-
const _r1 = i0.ɵɵgetCurrentView();
|
|
758
|
+
} }, inputs: { SectionKey: "SectionKey", Indicators: "Indicators", ValidationSources: "ValidationSources", SectionName: "SectionName", Icon: "Icon", Form: "Form", FormContext: "FormContext", Variant: "Variant", BadgeCount: "BadgeCount", DefaultExpanded: "DefaultExpanded", InheritedFromEntity: "InheritedFromEntity", InheritedRecordPrimaryKey: "InheritedRecordPrimaryKey", Hidden: "Hidden", _deprecatedSectionKey: [0, "sectionKey", "_deprecatedSectionKey"], _deprecatedSectionName: [0, "sectionName", "_deprecatedSectionName"], _deprecatedIcon: [0, "icon", "_deprecatedIcon"], _deprecatedForm: [0, "form", "_deprecatedForm"], _deprecatedFormContext: [0, "formContext", "_deprecatedFormContext"] }, outputs: { DragStarted: "DragStarted", DragEnded: "DragEnded", PanelDrop: "PanelDrop", Navigate: "Navigate" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], ngContentSelectors: _c1, decls: 17, vars: 22, consts: [["panelContent", ""], [1, "mj-forms-panel"], [1, "mj-forms-panel-header", 3, "click"], ["title", "Drag to reorder sections", "draggable", "true", 1, "mj-forms-drag-handle"], [1, "mj-forms-panel-title-group"], [1, "mj-forms-panel-icon"], [1, "mj-forms-panel-title"], [3, "innerHTML"], ["role", "img", 1, "mj-forms-panel-dirty", 3, "title"], ["role", "img", 1, "mj-forms-panel-error-badge", 3, "title"], ["role", "img", 1, "mj-forms-panel-warning-badge", 3, "title"], [1, "mj-forms-row-count-badge", 3, "mj-forms-row-count-badge--zero"], [1, "mj-forms-inherited-badge", 3, "title"], [1, "mj-forms-panel-chevron"], [1, "mj-forms-panel-body"], [1, "mj-forms-panel-content"], ["title", "Drag to reorder sections", "draggable", "true", 1, "mj-forms-drag-handle", 3, "dragstart", "dragend", "click"], [1, "fa-solid", "fa-grip-vertical"], ["aria-hidden", "true", 1, "fa-solid", "fa-circle-exclamation"], ["aria-hidden", "true", 1, "fa-solid", "fa-triangle-exclamation"], [1, "mj-forms-row-count-badge"], [1, "mj-forms-inherited-badge", 3, "click", "title"], [1, "fa-solid", "fa-arrow-up"]], template: function MjCollapsiblePanelComponent_Template(rf, ctx) { if (rf & 1) {
|
|
534
759
|
i0.ɵɵprojectionDef();
|
|
535
760
|
i0.ɵɵelementStart(0, "div", 1)(1, "div", 2);
|
|
536
|
-
i0.ɵɵlistener("click", function MjCollapsiblePanelComponent_Template_div_click_1_listener() {
|
|
761
|
+
i0.ɵɵlistener("click", function MjCollapsiblePanelComponent_Template_div_click_1_listener() { return ctx.Toggle(); });
|
|
537
762
|
i0.ɵɵconditionalCreate(2, MjCollapsiblePanelComponent_Conditional_2_Template, 2, 0, "div", 3);
|
|
538
763
|
i0.ɵɵelementStart(3, "div", 4);
|
|
539
764
|
i0.ɵɵelement(4, "i", 5);
|
|
540
765
|
i0.ɵɵelementStart(5, "h3", 6);
|
|
541
766
|
i0.ɵɵelement(6, "span", 7);
|
|
542
|
-
i0.ɵɵconditionalCreate(7, MjCollapsiblePanelComponent_Conditional_7_Template,
|
|
767
|
+
i0.ɵɵconditionalCreate(7, MjCollapsiblePanelComponent_Conditional_7_Template, 1, 2, "span", 8);
|
|
768
|
+
i0.ɵɵconditionalCreate(8, MjCollapsiblePanelComponent_Conditional_8_Template, 3, 3, "span", 9)(9, MjCollapsiblePanelComponent_Conditional_9_Template, 3, 3, "span", 10);
|
|
769
|
+
i0.ɵɵconditionalCreate(10, MjCollapsiblePanelComponent_Conditional_10_Template, 2, 3, "span", 11);
|
|
543
770
|
i0.ɵɵelementEnd()();
|
|
544
|
-
i0.ɵɵconditionalCreate(
|
|
545
|
-
i0.ɵɵconditionalCreate(
|
|
771
|
+
i0.ɵɵconditionalCreate(11, MjCollapsiblePanelComponent_Conditional_11_Template, 3, 2, "a", 12);
|
|
772
|
+
i0.ɵɵconditionalCreate(12, MjCollapsiblePanelComponent_Conditional_12_Template, 2, 2, "div", 13);
|
|
546
773
|
i0.ɵɵelementEnd();
|
|
547
|
-
i0.ɵɵelementStart(
|
|
548
|
-
i0.ɵɵprojection(
|
|
774
|
+
i0.ɵɵelementStart(13, "div", 14)(14, "div", 15, 0);
|
|
775
|
+
i0.ɵɵprojection(16);
|
|
549
776
|
i0.ɵɵelementEnd()()();
|
|
550
777
|
} if (rf & 2) {
|
|
551
778
|
i0.ɵɵclassProp("mj-forms-panel--related", ctx.Variant === "related-entity")("mj-forms-panel--inherited", ctx.Variant === "inherited");
|
|
@@ -560,22 +787,30 @@ export class MjCollapsiblePanelComponent {
|
|
|
560
787
|
i0.ɵɵadvance(2);
|
|
561
788
|
i0.ɵɵproperty("innerHTML", ctx.DisplayName, i0.ɵɵsanitizeHtml);
|
|
562
789
|
i0.ɵɵadvance();
|
|
563
|
-
i0.ɵɵconditional(ctx.
|
|
790
|
+
i0.ɵɵconditional(ctx.SectionDirtyCount > 0 ? 7 : -1);
|
|
791
|
+
i0.ɵɵadvance();
|
|
792
|
+
i0.ɵɵconditional(ctx.SectionErrorCount > 0 ? 8 : ctx.SectionWarningCount > 0 ? 9 : -1);
|
|
793
|
+
i0.ɵɵadvance(2);
|
|
794
|
+
i0.ɵɵconditional(ctx.BadgeCount !== undefined ? 10 : -1);
|
|
564
795
|
i0.ɵɵadvance();
|
|
565
|
-
i0.ɵɵconditional(ctx.Variant === "inherited" && ctx.InheritedFromEntity ?
|
|
796
|
+
i0.ɵɵconditional(ctx.Variant === "inherited" && ctx.InheritedFromEntity ? 11 : -1);
|
|
566
797
|
i0.ɵɵadvance();
|
|
567
|
-
i0.ɵɵconditional(ctx.Collapsible ?
|
|
798
|
+
i0.ɵɵconditional(ctx.Collapsible ? 12 : -1);
|
|
568
799
|
i0.ɵɵadvance();
|
|
569
800
|
i0.ɵɵclassProp("mj-forms-panel-body--collapsed", !ctx.Expanded);
|
|
570
801
|
i0.ɵɵadvance();
|
|
571
802
|
i0.ɵɵstyleProp("height", ctx.PanelContentHeight, "px");
|
|
572
|
-
} }, styles: ["mj-collapsible-panel {\n display: block;\n}\n\nmj-collapsible-panel.mj-search-hidden {\n display: none;\n}\n\nmj-collapsible-panel.mj-drag-over {\n border: 2px dashed var(--mj-brand-primary);\n border-radius: var(--mj-radius-md);\n}\n\n/* ============================================\n PANEL CARD\n ============================================ */\n.mj-forms-panel {\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-sm);\n border: 1px solid var(--mj-border-default);\n overflow: hidden;\n transition: all 0.3s ease;\n}\n\n/* ============================================\n HEADER\n ============================================ */\n.mj-forms-panel-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 20px;\n background: var(--mj-bg-surface-elevated);\n border-bottom: 2px solid var(--mj-border-default);\n cursor: pointer;\n user-select: none;\n transition: all 0.3s ease;\n}\n\n.mj-forms-panel-header:hover {\n background: var(--mj-bg-surface-hover);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n/* Static header \u2014 sections locked open (Config.collapsibleSections === false) */\n.mj-forms-panel-header--static {\n cursor: default;\n}\n\n.mj-forms-panel-header--static:hover {\n background: var(--mj-bg-surface-elevated);\n border-bottom-color: var(--mj-border-default);\n}\n\n/* Drag handle */\n.mj-forms-drag-handle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 0;\n height: 24px;\n color: var(--mj-text-muted);\n cursor: grab;\n opacity: 0;\n overflow: hidden;\n transition: width var(--mj-transition-base) ease, opacity var(--mj-transition-base) ease;\n flex-shrink: 0;\n}\n\n.mj-forms-panel-header:hover .mj-forms-drag-handle {\n width: 24px;\n opacity: 1;\n}\n\n.mj-forms-drag-handle:hover {\n color: var(--mj-brand-primary);\n}\n\n.mj-forms-drag-handle:active {\n cursor: grabbing;\n}\n\nmj-collapsible-panel.mj-dragging .mj-forms-drag-handle {\n width: 24px;\n opacity: 1;\n color: var(--mj-brand-primary);\n}\n\n/* Title group */\n.mj-forms-panel-title-group {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n}\n\n.mj-forms-panel-icon {\n font-size: 18px;\n color: var(--mj-brand-primary);\n width: 24px;\n text-align: center;\n}\n\n.mj-forms-panel-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mj-forms-panel-chevron {\n color: var(--mj-text-secondary);\n font-size: 14px;\n transition: transform 0.3s ease;\n padding: 4px;\n}\n\n/* Row count badge */\n.mj-forms-row-count-badge {\n background: var(--mj-status-success);\n color: var(--mj-text-inverse);\n padding: 2px 8px;\n border-radius: var(--mj-radius-full);\n font-size: 12px;\n font-weight: 600;\n margin-left: 8px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n line-height: 1;\n}\n\n.mj-forms-row-count-badge--zero {\n background: var(--mj-text-muted);\n}\n\n/* ============================================\n INHERITED VARIANT (IS-A parent fields)\n ============================================ */\n.mj-forms-panel--inherited {\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-header {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface-elevated));\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-header:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface-elevated));\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-icon {\n color: var(--mj-brand-primary);\n}\n\n.mj-forms-inherited-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n font-weight: 600;\n padding: 3px 10px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n cursor: pointer;\n transition: all var(--mj-transition-base) ease;\n text-decoration: none;\n}\n\n.mj-forms-inherited-badge:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 18%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 6px color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n}\n\n.mj-forms-inherited-badge i {\n font-size: 10px;\n}\n\n/* ============================================\n RELATED ENTITY VARIANT\n ============================================ */\n.mj-forms-panel--related {\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.mj-forms-panel--related .mj-forms-panel-header {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface-elevated));\n}\n\n.mj-forms-panel--related .mj-forms-panel-header:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface-elevated));\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.mj-forms-panel--related .mj-forms-panel-icon {\n color: var(--mj-brand-primary);\n}\n\n/* Related entity content hugs the grid (toolbar + header + rows).\n A fixed 400px box left a hollow slab when AG Grid's viewport was 0,\n and a persisted 0/toolbar height clipped the rows after expand. */\n.mj-forms-panel--related .mj-forms-panel-content {\n height: auto;\n padding: 0;\n overflow: hidden;\n resize: vertical;\n min-height: 0;\n max-height: 800px;\n}\n\n/* AG Grid must fill the 400px / leftover box. Do not stretch every\n descendant \u2014 custom widgets (relationship list) put their section\n header as the first child, and height:100% on it parks the title\n mid-column and hides the list below the fold. */\n.mj-forms-panel--related .mj-forms-panel-content > mj-explorer-entity-data-grid,\n.mj-forms-panel--related .mj-forms-panel-content > .ng-star-inserted > mj-explorer-entity-data-grid {\n height: 100%;\n}\n\n/* Related entity panels need the body to not clip the resize handle */\n.mj-forms-panel--related .mj-forms-panel-body {\n overflow: visible;\n}\n\n/* ============================================\n BODY (collapsible)\n ============================================ */\n.mj-forms-panel-body {\n max-height: 2000px;\n overflow: hidden;\n transition: max-height 0.4s ease, padding 0.4s ease, opacity 0.3s ease;\n opacity: 1;\n}\n\n.mj-forms-panel-body--collapsed {\n max-height: 0 !important;\n padding: 0 !important;\n opacity: 0 !important;\n overflow: hidden !important;\n pointer-events: none;\n}\n\n.mj-forms-panel-body--collapsed .mj-forms-panel-content {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n max-height: 0 !important;\n overflow: hidden !important;\n}\n\n.mj-forms-panel-content {\n padding: 20px;\n overflow: auto;\n max-height: 600px;\n}\n\n/* ============================================\n SEARCH HIGHLIGHTING\n ============================================ */\n.mj-forms-panel-title ::ng-deep .mj-forms-search-highlight {\n /* Muted, theme-aware highlight (semantic token) \u2014 consistent with the field\n label + FK dropdown highlights. Replaces the old bright-yellow primitive. */\n background-color: var(--mj-search-highlight-bg);\n color: var(--mj-search-highlight-text);\n border-radius: 2px;\n padding: 0 1px;\n font-weight: 600;\n}\n"], encapsulation: 2, changeDetection: 0 });
|
|
803
|
+
} }, styles: ["mj-collapsible-panel {\n display: block;\n}\n\nmj-collapsible-panel.mj-search-hidden {\n display: none;\n}\n\nmj-collapsible-panel.mj-drag-over {\n border: 2px dashed var(--mj-brand-primary);\n border-radius: var(--mj-radius-md);\n}\n\n/* ============================================\n PANEL CARD\n ============================================ */\n.mj-forms-panel {\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-sm);\n border: 1px solid var(--mj-border-default);\n overflow: hidden;\n transition: all 0.3s ease;\n}\n\n/* ============================================\n HEADER\n ============================================ */\n.mj-forms-panel-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 20px;\n background: var(--mj-bg-surface-elevated);\n border-bottom: 2px solid var(--mj-border-default);\n cursor: pointer;\n user-select: none;\n transition: all 0.3s ease;\n}\n\n.mj-forms-panel-header:hover {\n background: var(--mj-bg-surface-hover);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n/* Static header \u2014 sections locked open (Config.collapsibleSections === false) */\n.mj-forms-panel-header--static {\n cursor: default;\n}\n\n.mj-forms-panel-header--static:hover {\n background: var(--mj-bg-surface-elevated);\n border-bottom-color: var(--mj-border-default);\n}\n\n/* Drag handle */\n.mj-forms-drag-handle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 0;\n height: 24px;\n color: var(--mj-text-muted);\n cursor: grab;\n opacity: 0;\n overflow: hidden;\n transition: width var(--mj-transition-base) ease, opacity var(--mj-transition-base) ease;\n flex-shrink: 0;\n}\n\n.mj-forms-panel-header:hover .mj-forms-drag-handle {\n width: 24px;\n opacity: 1;\n}\n\n.mj-forms-drag-handle:hover {\n color: var(--mj-brand-primary);\n}\n\n.mj-forms-drag-handle:active {\n cursor: grabbing;\n}\n\nmj-collapsible-panel.mj-dragging .mj-forms-drag-handle {\n width: 24px;\n opacity: 1;\n color: var(--mj-brand-primary);\n}\n\n/* Title group */\n.mj-forms-panel-title-group {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n}\n\n.mj-forms-panel-icon {\n font-size: 18px;\n color: var(--mj-brand-primary);\n width: 24px;\n text-align: center;\n}\n\n.mj-forms-panel-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mj-forms-panel-chevron {\n color: var(--mj-text-secondary);\n font-size: 14px;\n transition: transform 0.3s ease;\n padding: 4px;\n}\n\n/* Row count badge */\n.mj-forms-row-count-badge {\n background: var(--mj-status-success);\n color: var(--mj-text-inverse);\n padding: 2px 8px;\n border-radius: var(--mj-radius-full);\n font-size: 12px;\n font-weight: 600;\n margin-left: 8px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n line-height: 1;\n}\n\n.mj-forms-row-count-badge--zero {\n background: var(--mj-text-muted);\n}\n\n/* ============================================\n SECTION INDICATORS\n Unsaved-changes dot + invalid-field count, derived from the section's own\n fields so a collapsed accordion header (or the left-nav rail, which reads\n the same counts) says WHERE the edited / failing field is.\n ============================================ */\n\n/* Same 6px amber dot an edited field shows after its label. */\n.mj-forms-panel-dirty {\n display: inline-block;\n width: var(--mj-space-1-5);\n height: var(--mj-space-1-5);\n border-radius: var(--mj-radius-full);\n background: var(--mj-status-warning);\n margin-left: var(--mj-space-2);\n vertical-align: middle;\n}\n\n.mj-forms-panel-error-badge,\n.mj-forms-panel-warning-badge {\n padding: var(--mj-space-0-5) var(--mj-space-2);\n border-radius: var(--mj-radius-full);\n font-size: var(--mj-text-xs);\n font-weight: var(--mj-font-semibold);\n margin-left: var(--mj-space-2);\n display: inline-flex;\n align-items: center;\n gap: var(--mj-space-1);\n justify-content: center;\n min-width: var(--mj-space-6);\n line-height: 1;\n vertical-align: middle;\n}\n\n/* Same colour as the failing field's underline. */\n.mj-forms-panel-error-badge {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n}\n\n.mj-forms-panel-warning-badge {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n}\n\n.mj-forms-panel-error-badge i,\n.mj-forms-panel-warning-badge i {\n font-size: var(--mj-text-xs);\n}\n\n/* Card edge + header tint while the section holds an invalid field \u2014 the same\n left-edge treatment the related / inherited variants use. Not applied to the\n selected left-nav section (`.mj-chrome-show`): there the rail is the header\n and the card chrome is stripped. */\nmj-collapsible-panel.mj-panel-has-errors:not(.mj-chrome-show) > .mj-forms-panel {\n border-left: 3px solid var(--mj-status-error);\n}\n\nmj-collapsible-panel.mj-panel-has-errors:not(.mj-chrome-show) > .mj-forms-panel > .mj-forms-panel-header {\n background: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface-elevated));\n}\n\nmj-collapsible-panel.mj-panel-has-warnings:not(.mj-chrome-show) > .mj-forms-panel {\n border-left: 3px solid var(--mj-status-warning);\n}\n\n/* ============================================\n INHERITED VARIANT (IS-A parent fields)\n ============================================ */\n.mj-forms-panel--inherited {\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-header {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface-elevated));\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-header:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface-elevated));\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-icon {\n color: var(--mj-brand-primary);\n}\n\n.mj-forms-inherited-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n font-weight: 600;\n padding: 3px 10px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n cursor: pointer;\n transition: all var(--mj-transition-base) ease;\n text-decoration: none;\n}\n\n.mj-forms-inherited-badge:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 18%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 6px color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n}\n\n.mj-forms-inherited-badge i {\n font-size: 10px;\n}\n\n/* ============================================\n RELATED ENTITY VARIANT\n ============================================ */\n.mj-forms-panel--related {\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.mj-forms-panel--related .mj-forms-panel-header {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface-elevated));\n}\n\n.mj-forms-panel--related .mj-forms-panel-header:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface-elevated));\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.mj-forms-panel--related .mj-forms-panel-icon {\n color: var(--mj-brand-primary);\n}\n\n/* Related entity content hugs the grid (toolbar + header + rows).\n A fixed 400px box left a hollow slab when AG Grid's viewport was 0,\n and a persisted 0/toolbar height clipped the rows after expand. */\n.mj-forms-panel--related .mj-forms-panel-content {\n height: auto;\n padding: 0;\n overflow: hidden;\n resize: vertical;\n min-height: 0;\n max-height: 800px;\n}\n\n/* AG Grid must fill the 400px / leftover box. Do not stretch every\n descendant \u2014 custom widgets (relationship list) put their section\n header as the first child, and height:100% on it parks the title\n mid-column and hides the list below the fold. */\n.mj-forms-panel--related .mj-forms-panel-content > mj-explorer-entity-data-grid,\n.mj-forms-panel--related .mj-forms-panel-content > .ng-star-inserted > mj-explorer-entity-data-grid {\n height: 100%;\n}\n\n/* Related entity panels need the body to not clip the resize handle */\n.mj-forms-panel--related .mj-forms-panel-body {\n overflow: visible;\n}\n\n/* ============================================\n BODY (collapsible)\n ============================================ */\n.mj-forms-panel-body {\n max-height: 2000px;\n overflow: hidden;\n transition: max-height 0.4s ease, padding 0.4s ease, opacity 0.3s ease;\n opacity: 1;\n}\n\n.mj-forms-panel-body--collapsed {\n max-height: 0 !important;\n padding: 0 !important;\n opacity: 0 !important;\n overflow: hidden !important;\n pointer-events: none;\n}\n\n.mj-forms-panel-body--collapsed .mj-forms-panel-content {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n max-height: 0 !important;\n overflow: hidden !important;\n}\n\n.mj-forms-panel-content {\n padding: 20px;\n overflow: auto;\n max-height: 600px;\n}\n\n/* ============================================\n SEARCH HIGHLIGHTING\n ============================================ */\n.mj-forms-panel-title ::ng-deep .mj-forms-search-highlight {\n /* Muted, theme-aware highlight (semantic token) \u2014 consistent with the field\n label + FK dropdown highlights. Replaces the old bright-yellow primitive. */\n background-color: var(--mj-search-highlight-bg);\n color: var(--mj-search-highlight-text);\n border-radius: 2px;\n padding: 0 1px;\n font-weight: 600;\n}\n"], encapsulation: 2, changeDetection: 0 });
|
|
573
804
|
}
|
|
574
805
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MjCollapsiblePanelComponent, [{
|
|
575
806
|
type: Component,
|
|
576
|
-
args: [{ standalone: false, selector: 'mj-collapsible-panel', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"mj-forms-panel\"\n [class.mj-forms-panel--related]=\"Variant === 'related-entity'\"\n [class.mj-forms-panel--inherited]=\"Variant === 'inherited'\"\n [attr.data-section-key]=\"SectionKey\">\n\n <!-- Panel Header -->\n <div class=\"mj-forms-panel-header\"\n [class.mj-forms-panel-header--static]=\"!Collapsible\"\n [attr.role]=\"Collapsible ? 'button' : null\"\n [attr.tabindex]=\"Collapsible ? 0 : null\"\n (click)=\"Toggle()\">\n <!-- Drag Handle (hidden when reorder disabled) -->\n @if (ReorderAllowed) {\n <div class=\"mj-forms-drag-handle\"\n title=\"Drag to reorder sections\"\n draggable=\"true\"\n (dragstart)=\"OnDragStart($event)\"\n (dragend)=\"OnDragEnd()\"\n (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-grip-vertical\"></i>\n </div>\n }\n\n <!-- Icon + Title -->\n <div class=\"mj-forms-panel-title-group\">\n <i [class]=\"Icon\" class=\"mj-forms-panel-icon\"></i>\n <h3 class=\"mj-forms-panel-title\">\n <span [innerHTML]=\"DisplayName\"></span>\n <!-- Row count badge for related entities -->\n @if (BadgeCount !== undefined) {\n <span class=\"mj-forms-row-count-badge\" [class.mj-forms-row-count-badge--zero]=\"BadgeCount === 0\">\n {{ BadgeCount }}\n </span>\n }\n </h3>\n </div>\n\n <!-- Inherited From badge (IS-A) -->\n @if (Variant === 'inherited' && InheritedFromEntity) {\n <a class=\"mj-forms-inherited-badge\" (click)=\"OnInheritedBadgeClick($event)\"\n [title]=\"'View parent record: ' + InheritedFromEntity\">\n <i class=\"fa-solid fa-arrow-up\"></i>\n Inherited from {{ InheritedFromEntity }}\n </a>\n }\n\n <!-- Chevron (hidden when sections are locked open) -->\n @if (Collapsible) {\n <div class=\"mj-forms-panel-chevron\">\n <i [class]=\"Expanded ? 'fa fa-chevron-up' : 'fa fa-chevron-down'\"></i>\n </div>\n }\n </div>\n\n <!-- Panel Body -->\n <div class=\"mj-forms-panel-body\" [class.mj-forms-panel-body--collapsed]=\"!Expanded\">\n <div class=\"mj-forms-panel-content\"\n #panelContent\n [style.height.px]=\"PanelContentHeight\">\n <ng-content></ng-content>\n </div>\n </div>\n</div>\n", styles: ["mj-collapsible-panel {\n display: block;\n}\n\nmj-collapsible-panel.mj-search-hidden {\n display: none;\n}\n\nmj-collapsible-panel.mj-drag-over {\n border: 2px dashed var(--mj-brand-primary);\n border-radius: var(--mj-radius-md);\n}\n\n/* ============================================\n PANEL CARD\n ============================================ */\n.mj-forms-panel {\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-sm);\n border: 1px solid var(--mj-border-default);\n overflow: hidden;\n transition: all 0.3s ease;\n}\n\n/* ============================================\n HEADER\n ============================================ */\n.mj-forms-panel-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 20px;\n background: var(--mj-bg-surface-elevated);\n border-bottom: 2px solid var(--mj-border-default);\n cursor: pointer;\n user-select: none;\n transition: all 0.3s ease;\n}\n\n.mj-forms-panel-header:hover {\n background: var(--mj-bg-surface-hover);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n/* Static header \u2014 sections locked open (Config.collapsibleSections === false) */\n.mj-forms-panel-header--static {\n cursor: default;\n}\n\n.mj-forms-panel-header--static:hover {\n background: var(--mj-bg-surface-elevated);\n border-bottom-color: var(--mj-border-default);\n}\n\n/* Drag handle */\n.mj-forms-drag-handle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 0;\n height: 24px;\n color: var(--mj-text-muted);\n cursor: grab;\n opacity: 0;\n overflow: hidden;\n transition: width var(--mj-transition-base) ease, opacity var(--mj-transition-base) ease;\n flex-shrink: 0;\n}\n\n.mj-forms-panel-header:hover .mj-forms-drag-handle {\n width: 24px;\n opacity: 1;\n}\n\n.mj-forms-drag-handle:hover {\n color: var(--mj-brand-primary);\n}\n\n.mj-forms-drag-handle:active {\n cursor: grabbing;\n}\n\nmj-collapsible-panel.mj-dragging .mj-forms-drag-handle {\n width: 24px;\n opacity: 1;\n color: var(--mj-brand-primary);\n}\n\n/* Title group */\n.mj-forms-panel-title-group {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n}\n\n.mj-forms-panel-icon {\n font-size: 18px;\n color: var(--mj-brand-primary);\n width: 24px;\n text-align: center;\n}\n\n.mj-forms-panel-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mj-forms-panel-chevron {\n color: var(--mj-text-secondary);\n font-size: 14px;\n transition: transform 0.3s ease;\n padding: 4px;\n}\n\n/* Row count badge */\n.mj-forms-row-count-badge {\n background: var(--mj-status-success);\n color: var(--mj-text-inverse);\n padding: 2px 8px;\n border-radius: var(--mj-radius-full);\n font-size: 12px;\n font-weight: 600;\n margin-left: 8px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n line-height: 1;\n}\n\n.mj-forms-row-count-badge--zero {\n background: var(--mj-text-muted);\n}\n\n/* ============================================\n INHERITED VARIANT (IS-A parent fields)\n ============================================ */\n.mj-forms-panel--inherited {\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-header {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface-elevated));\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-header:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface-elevated));\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-icon {\n color: var(--mj-brand-primary);\n}\n\n.mj-forms-inherited-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n font-weight: 600;\n padding: 3px 10px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n cursor: pointer;\n transition: all var(--mj-transition-base) ease;\n text-decoration: none;\n}\n\n.mj-forms-inherited-badge:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 18%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 6px color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n}\n\n.mj-forms-inherited-badge i {\n font-size: 10px;\n}\n\n/* ============================================\n RELATED ENTITY VARIANT\n ============================================ */\n.mj-forms-panel--related {\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.mj-forms-panel--related .mj-forms-panel-header {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface-elevated));\n}\n\n.mj-forms-panel--related .mj-forms-panel-header:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface-elevated));\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.mj-forms-panel--related .mj-forms-panel-icon {\n color: var(--mj-brand-primary);\n}\n\n/* Related entity content hugs the grid (toolbar + header + rows).\n A fixed 400px box left a hollow slab when AG Grid's viewport was 0,\n and a persisted 0/toolbar height clipped the rows after expand. */\n.mj-forms-panel--related .mj-forms-panel-content {\n height: auto;\n padding: 0;\n overflow: hidden;\n resize: vertical;\n min-height: 0;\n max-height: 800px;\n}\n\n/* AG Grid must fill the 400px / leftover box. Do not stretch every\n descendant \u2014 custom widgets (relationship list) put their section\n header as the first child, and height:100% on it parks the title\n mid-column and hides the list below the fold. */\n.mj-forms-panel--related .mj-forms-panel-content > mj-explorer-entity-data-grid,\n.mj-forms-panel--related .mj-forms-panel-content > .ng-star-inserted > mj-explorer-entity-data-grid {\n height: 100%;\n}\n\n/* Related entity panels need the body to not clip the resize handle */\n.mj-forms-panel--related .mj-forms-panel-body {\n overflow: visible;\n}\n\n/* ============================================\n BODY (collapsible)\n ============================================ */\n.mj-forms-panel-body {\n max-height: 2000px;\n overflow: hidden;\n transition: max-height 0.4s ease, padding 0.4s ease, opacity 0.3s ease;\n opacity: 1;\n}\n\n.mj-forms-panel-body--collapsed {\n max-height: 0 !important;\n padding: 0 !important;\n opacity: 0 !important;\n overflow: hidden !important;\n pointer-events: none;\n}\n\n.mj-forms-panel-body--collapsed .mj-forms-panel-content {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n max-height: 0 !important;\n overflow: hidden !important;\n}\n\n.mj-forms-panel-content {\n padding: 20px;\n overflow: auto;\n max-height: 600px;\n}\n\n/* ============================================\n SEARCH HIGHLIGHTING\n ============================================ */\n.mj-forms-panel-title ::ng-deep .mj-forms-search-highlight {\n /* Muted, theme-aware highlight (semantic token) \u2014 consistent with the field\n label + FK dropdown highlights. Replaces the old bright-yellow primitive. */\n background-color: var(--mj-search-highlight-bg);\n color: var(--mj-search-highlight-text);\n border-radius: 2px;\n padding: 0 1px;\n font-weight: 600;\n}\n"] }]
|
|
807
|
+
args: [{ standalone: false, selector: 'mj-collapsible-panel', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"mj-forms-panel\"\n [class.mj-forms-panel--related]=\"Variant === 'related-entity'\"\n [class.mj-forms-panel--inherited]=\"Variant === 'inherited'\"\n [attr.data-section-key]=\"SectionKey\">\n\n <!-- Panel Header -->\n <div class=\"mj-forms-panel-header\"\n [class.mj-forms-panel-header--static]=\"!Collapsible\"\n [attr.role]=\"Collapsible ? 'button' : null\"\n [attr.tabindex]=\"Collapsible ? 0 : null\"\n (click)=\"Toggle()\">\n <!-- Drag Handle (hidden when reorder disabled) -->\n @if (ReorderAllowed) {\n <div class=\"mj-forms-drag-handle\"\n title=\"Drag to reorder sections\"\n draggable=\"true\"\n (dragstart)=\"OnDragStart($event)\"\n (dragend)=\"OnDragEnd()\"\n (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-grip-vertical\"></i>\n </div>\n }\n\n <!-- Icon + Title -->\n <div class=\"mj-forms-panel-title-group\">\n <i [class]=\"Icon\" class=\"mj-forms-panel-icon\"></i>\n <h3 class=\"mj-forms-panel-title\">\n <span [innerHTML]=\"DisplayName\"></span>\n <!-- Section indicators: unsaved-changes dot (same idiom as an edited field's\n label dot) and invalid-field count (same colour as the field's red underline).\n Derived from the projected fields \u2014 see SectionIndicators. -->\n @if (SectionDirtyCount > 0) {\n <span class=\"mj-forms-panel-dirty\"\n role=\"img\"\n [title]=\"SectionDirtyTitle\"\n [attr.aria-label]=\"SectionDirtyTitle\"></span>\n }\n @if (SectionErrorCount > 0) {\n <span class=\"mj-forms-panel-error-badge\"\n role=\"img\"\n [title]=\"SectionErrorTitle\"\n [attr.aria-label]=\"SectionErrorTitle\">\n <i class=\"fa-solid fa-circle-exclamation\" aria-hidden=\"true\"></i>{{ SectionErrorCount }}\n </span>\n } @else if (SectionWarningCount > 0) {\n <span class=\"mj-forms-panel-warning-badge\"\n role=\"img\"\n [title]=\"SectionWarningTitle\"\n [attr.aria-label]=\"SectionWarningTitle\">\n <i class=\"fa-solid fa-triangle-exclamation\" aria-hidden=\"true\"></i>{{ SectionWarningCount }}\n </span>\n }\n <!-- Row count badge for related entities -->\n @if (BadgeCount !== undefined) {\n <span class=\"mj-forms-row-count-badge\" [class.mj-forms-row-count-badge--zero]=\"BadgeCount === 0\">\n {{ BadgeCount }}\n </span>\n }\n </h3>\n </div>\n\n <!-- Inherited From badge (IS-A) -->\n @if (Variant === 'inherited' && InheritedFromEntity) {\n <a class=\"mj-forms-inherited-badge\" (click)=\"OnInheritedBadgeClick($event)\"\n [title]=\"'View parent record: ' + InheritedFromEntity\">\n <i class=\"fa-solid fa-arrow-up\"></i>\n Inherited from {{ InheritedFromEntity }}\n </a>\n }\n\n <!-- Chevron (hidden when sections are locked open) -->\n @if (Collapsible) {\n <div class=\"mj-forms-panel-chevron\">\n <i [class]=\"Expanded ? 'fa fa-chevron-up' : 'fa fa-chevron-down'\"></i>\n </div>\n }\n </div>\n\n <!-- Panel Body -->\n <div class=\"mj-forms-panel-body\" [class.mj-forms-panel-body--collapsed]=\"!Expanded\">\n <div class=\"mj-forms-panel-content\"\n #panelContent\n [style.height.px]=\"PanelContentHeight\">\n <ng-content></ng-content>\n </div>\n </div>\n</div>\n", styles: ["mj-collapsible-panel {\n display: block;\n}\n\nmj-collapsible-panel.mj-search-hidden {\n display: none;\n}\n\nmj-collapsible-panel.mj-drag-over {\n border: 2px dashed var(--mj-brand-primary);\n border-radius: var(--mj-radius-md);\n}\n\n/* ============================================\n PANEL CARD\n ============================================ */\n.mj-forms-panel {\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-sm);\n border: 1px solid var(--mj-border-default);\n overflow: hidden;\n transition: all 0.3s ease;\n}\n\n/* ============================================\n HEADER\n ============================================ */\n.mj-forms-panel-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 20px;\n background: var(--mj-bg-surface-elevated);\n border-bottom: 2px solid var(--mj-border-default);\n cursor: pointer;\n user-select: none;\n transition: all 0.3s ease;\n}\n\n.mj-forms-panel-header:hover {\n background: var(--mj-bg-surface-hover);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n/* Static header \u2014 sections locked open (Config.collapsibleSections === false) */\n.mj-forms-panel-header--static {\n cursor: default;\n}\n\n.mj-forms-panel-header--static:hover {\n background: var(--mj-bg-surface-elevated);\n border-bottom-color: var(--mj-border-default);\n}\n\n/* Drag handle */\n.mj-forms-drag-handle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 0;\n height: 24px;\n color: var(--mj-text-muted);\n cursor: grab;\n opacity: 0;\n overflow: hidden;\n transition: width var(--mj-transition-base) ease, opacity var(--mj-transition-base) ease;\n flex-shrink: 0;\n}\n\n.mj-forms-panel-header:hover .mj-forms-drag-handle {\n width: 24px;\n opacity: 1;\n}\n\n.mj-forms-drag-handle:hover {\n color: var(--mj-brand-primary);\n}\n\n.mj-forms-drag-handle:active {\n cursor: grabbing;\n}\n\nmj-collapsible-panel.mj-dragging .mj-forms-drag-handle {\n width: 24px;\n opacity: 1;\n color: var(--mj-brand-primary);\n}\n\n/* Title group */\n.mj-forms-panel-title-group {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n}\n\n.mj-forms-panel-icon {\n font-size: 18px;\n color: var(--mj-brand-primary);\n width: 24px;\n text-align: center;\n}\n\n.mj-forms-panel-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mj-forms-panel-chevron {\n color: var(--mj-text-secondary);\n font-size: 14px;\n transition: transform 0.3s ease;\n padding: 4px;\n}\n\n/* Row count badge */\n.mj-forms-row-count-badge {\n background: var(--mj-status-success);\n color: var(--mj-text-inverse);\n padding: 2px 8px;\n border-radius: var(--mj-radius-full);\n font-size: 12px;\n font-weight: 600;\n margin-left: 8px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n line-height: 1;\n}\n\n.mj-forms-row-count-badge--zero {\n background: var(--mj-text-muted);\n}\n\n/* ============================================\n SECTION INDICATORS\n Unsaved-changes dot + invalid-field count, derived from the section's own\n fields so a collapsed accordion header (or the left-nav rail, which reads\n the same counts) says WHERE the edited / failing field is.\n ============================================ */\n\n/* Same 6px amber dot an edited field shows after its label. */\n.mj-forms-panel-dirty {\n display: inline-block;\n width: var(--mj-space-1-5);\n height: var(--mj-space-1-5);\n border-radius: var(--mj-radius-full);\n background: var(--mj-status-warning);\n margin-left: var(--mj-space-2);\n vertical-align: middle;\n}\n\n.mj-forms-panel-error-badge,\n.mj-forms-panel-warning-badge {\n padding: var(--mj-space-0-5) var(--mj-space-2);\n border-radius: var(--mj-radius-full);\n font-size: var(--mj-text-xs);\n font-weight: var(--mj-font-semibold);\n margin-left: var(--mj-space-2);\n display: inline-flex;\n align-items: center;\n gap: var(--mj-space-1);\n justify-content: center;\n min-width: var(--mj-space-6);\n line-height: 1;\n vertical-align: middle;\n}\n\n/* Same colour as the failing field's underline. */\n.mj-forms-panel-error-badge {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n}\n\n.mj-forms-panel-warning-badge {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n}\n\n.mj-forms-panel-error-badge i,\n.mj-forms-panel-warning-badge i {\n font-size: var(--mj-text-xs);\n}\n\n/* Card edge + header tint while the section holds an invalid field \u2014 the same\n left-edge treatment the related / inherited variants use. Not applied to the\n selected left-nav section (`.mj-chrome-show`): there the rail is the header\n and the card chrome is stripped. */\nmj-collapsible-panel.mj-panel-has-errors:not(.mj-chrome-show) > .mj-forms-panel {\n border-left: 3px solid var(--mj-status-error);\n}\n\nmj-collapsible-panel.mj-panel-has-errors:not(.mj-chrome-show) > .mj-forms-panel > .mj-forms-panel-header {\n background: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface-elevated));\n}\n\nmj-collapsible-panel.mj-panel-has-warnings:not(.mj-chrome-show) > .mj-forms-panel {\n border-left: 3px solid var(--mj-status-warning);\n}\n\n/* ============================================\n INHERITED VARIANT (IS-A parent fields)\n ============================================ */\n.mj-forms-panel--inherited {\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-header {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface-elevated));\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-header:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface-elevated));\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-icon {\n color: var(--mj-brand-primary);\n}\n\n.mj-forms-inherited-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n font-weight: 600;\n padding: 3px 10px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n cursor: pointer;\n transition: all var(--mj-transition-base) ease;\n text-decoration: none;\n}\n\n.mj-forms-inherited-badge:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 18%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 6px color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n}\n\n.mj-forms-inherited-badge i {\n font-size: 10px;\n}\n\n/* ============================================\n RELATED ENTITY VARIANT\n ============================================ */\n.mj-forms-panel--related {\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.mj-forms-panel--related .mj-forms-panel-header {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface-elevated));\n}\n\n.mj-forms-panel--related .mj-forms-panel-header:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface-elevated));\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.mj-forms-panel--related .mj-forms-panel-icon {\n color: var(--mj-brand-primary);\n}\n\n/* Related entity content hugs the grid (toolbar + header + rows).\n A fixed 400px box left a hollow slab when AG Grid's viewport was 0,\n and a persisted 0/toolbar height clipped the rows after expand. */\n.mj-forms-panel--related .mj-forms-panel-content {\n height: auto;\n padding: 0;\n overflow: hidden;\n resize: vertical;\n min-height: 0;\n max-height: 800px;\n}\n\n/* AG Grid must fill the 400px / leftover box. Do not stretch every\n descendant \u2014 custom widgets (relationship list) put their section\n header as the first child, and height:100% on it parks the title\n mid-column and hides the list below the fold. */\n.mj-forms-panel--related .mj-forms-panel-content > mj-explorer-entity-data-grid,\n.mj-forms-panel--related .mj-forms-panel-content > .ng-star-inserted > mj-explorer-entity-data-grid {\n height: 100%;\n}\n\n/* Related entity panels need the body to not clip the resize handle */\n.mj-forms-panel--related .mj-forms-panel-body {\n overflow: visible;\n}\n\n/* ============================================\n BODY (collapsible)\n ============================================ */\n.mj-forms-panel-body {\n max-height: 2000px;\n overflow: hidden;\n transition: max-height 0.4s ease, padding 0.4s ease, opacity 0.3s ease;\n opacity: 1;\n}\n\n.mj-forms-panel-body--collapsed {\n max-height: 0 !important;\n padding: 0 !important;\n opacity: 0 !important;\n overflow: hidden !important;\n pointer-events: none;\n}\n\n.mj-forms-panel-body--collapsed .mj-forms-panel-content {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n max-height: 0 !important;\n overflow: hidden !important;\n}\n\n.mj-forms-panel-content {\n padding: 20px;\n overflow: auto;\n max-height: 600px;\n}\n\n/* ============================================\n SEARCH HIGHLIGHTING\n ============================================ */\n.mj-forms-panel-title ::ng-deep .mj-forms-search-highlight {\n /* Muted, theme-aware highlight (semantic token) \u2014 consistent with the field\n label + FK dropdown highlights. Replaces the old bright-yellow primitive. */\n background-color: var(--mj-search-highlight-bg);\n color: var(--mj-search-highlight-text);\n border-radius: 2px;\n padding: 0 1px;\n font-weight: 600;\n}\n"] }]
|
|
577
808
|
}], null, { SectionKey: [{
|
|
578
809
|
type: Input
|
|
810
|
+
}], Indicators: [{
|
|
811
|
+
type: Input
|
|
812
|
+
}], ValidationSources: [{
|
|
813
|
+
type: Input
|
|
579
814
|
}], SectionName: [{
|
|
580
815
|
type: Input
|
|
581
816
|
}], Icon: [{
|
|
@@ -631,6 +866,12 @@ export class MjCollapsiblePanelComponent {
|
|
|
631
866
|
}], HostIcon: [{
|
|
632
867
|
type: HostBinding,
|
|
633
868
|
args: ['attr.data-icon']
|
|
869
|
+
}], HostDirtyCount: [{
|
|
870
|
+
type: HostBinding,
|
|
871
|
+
args: ['attr.data-dirty-count']
|
|
872
|
+
}], HostErrorCount: [{
|
|
873
|
+
type: HostBinding,
|
|
874
|
+
args: ['attr.data-error-count']
|
|
634
875
|
}], HostClass: [{
|
|
635
876
|
type: HostBinding,
|
|
636
877
|
args: ['class']
|
|
@@ -650,5 +891,5 @@ export class MjCollapsiblePanelComponent {
|
|
|
650
891
|
type: HostListener,
|
|
651
892
|
args: ['drop', ['$event']]
|
|
652
893
|
}] }); })();
|
|
653
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MjCollapsiblePanelComponent, { className: "MjCollapsiblePanelComponent", filePath: "src/lib/panel/collapsible-panel.component.ts", lineNumber:
|
|
894
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MjCollapsiblePanelComponent, { className: "MjCollapsiblePanelComponent", filePath: "src/lib/panel/collapsible-panel.component.ts", lineNumber: 73 }); })();
|
|
654
895
|
//# sourceMappingURL=collapsible-panel.component.js.map
|