@memberjunction/ng-base-forms 5.43.0 → 5.44.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.
@@ -1 +1 @@
1
- {"version":3,"file":"explorer-entity-data-grid.component.js","sourceRoot":"","sources":["../../src/lib/explorer-entity-data-grid.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, ChangeDetectorRef, NgZone, AfterViewInit, OnDestroy, inject } from '@angular/core';\nimport { RunViewParams } from '@memberjunction/core';\nimport {\n EntityDataGridComponent,\n AfterRowDoubleClickEventArgs,\n AfterRowClickEventArgs,\n AfterDataLoadEventArgs,\n GridToolbarConfig,\n GridSelectionMode,\n buildCompositeKey\n} from '@memberjunction/ng-entity-viewer';\nimport { EntityInfo } from '@memberjunction/core';\nimport { FormNavigationEvent } from './types/navigation-events';\n\n/**\n * Wrapper for EntityDataGridComponent that emits navigation events on row double-click.\n * The host application subscribes to Navigate events and maps them to its routing system.\n *\n * This component is used by CodeGen for related entity grids in forms.\n */\n@Component({\n standalone: false,\n selector: 'mj-explorer-entity-data-grid',\n template: `\n <mj-entity-data-grid\n #innerGrid\n [Params]=\"Params\"\n [NewRecordValues]=\"NewRecordValues\"\n [AllowLoad]=\"EffectiveAllowLoad\"\n [ShowToolbar]=\"ShowToolbar\"\n [Height]=\"Height\"\n [ToolbarConfig]=\"ToolbarConfig\"\n [SelectionMode]=\"SelectionMode\"\n (AfterRowDoubleClick)=\"onRowDoubleClick($event)\"\n (AfterRowClick)=\"onRowClick($event)\"\n (AfterDataLoad)=\"onDataLoad($event)\"\n (NewRecordTabRequested)=\"onNewRecordTabRequested($event)\">\n </mj-entity-data-grid>\n `,\n styles: [`\n :host {\n display: block;\n height: 100%;\n width: 100%;\n }\n `]\n})\nexport class ExplorerEntityDataGridComponent implements AfterViewInit, OnDestroy {\n @ViewChild('innerGrid') innerGrid!: EntityDataGridComponent;\n\n private elementRef = inject(ElementRef);\n private cdr = inject(ChangeDetectorRef);\n private ngZone = inject(NgZone);\n\n // Pass-through inputs from EntityDataGridComponent\n @Input() Params: RunViewParams | null = null;\n @Input() NewRecordValues: Record<string, unknown> = {};\n @Input() AllowLoad: boolean = true;\n @Input() ShowToolbar: boolean = true;\n @Input() Height: number | 'auto' | 'fit-content' = 'auto';\n @Input() ToolbarConfig: GridToolbarConfig = {};\n @Input() SelectionMode: GridSelectionMode = 'single';\n\n /**\n * When true (default), the inner grid does not fetch until this component's host\n * element first scrolls into the viewport. This prevents related-entity grids on a\n * form — which are always rendered in the DOM (the collapsible panel hides them via\n * CSS, not @if) — from firing a RunView on form open while they're collapsed or below\n * the fold. The first time the panel becomes visible (scrolled into view and expanded,\n * giving the host non-zero area), the grid loads with its normal spinner.\n * Set to false to restore eager loading for consumers that always want immediate data.\n */\n @Input() DeferLoadUntilVisible: boolean = true;\n\n /** True once the host has first intersected the viewport (or deferral is disabled / unsupported). */\n private _hasBeenVisible = false;\n private _visibilityObserver?: IntersectionObserver;\n\n /**\n * Effective AllowLoad passed to the inner grid: the form's AllowLoad AND\n * (deferral disabled OR the panel has been seen at least once).\n */\n get EffectiveAllowLoad(): boolean {\n return this.AllowLoad && (!this.DeferLoadUntilVisible || this._hasBeenVisible);\n }\n\n ngAfterViewInit(): void {\n if (!this.DeferLoadUntilVisible || typeof IntersectionObserver === 'undefined') {\n // Deferral off or unsupported environment — preserve eager-load behavior.\n this._hasBeenVisible = true;\n return;\n }\n\n // Observe outside Angular so scroll churn doesn't trigger change detection;\n // we re-enter the zone only on the one-shot \"became visible\" transition.\n this.ngZone.runOutsideAngular(() => {\n this._visibilityObserver = new IntersectionObserver(\n (entries) => {\n if (entries.some(e => e.isIntersecting)) {\n this.onBecameVisible();\n }\n },\n // Small positive rootMargin pre-loads just before the panel scrolls fully into view.\n { root: null, rootMargin: '200px', threshold: 0 }\n );\n this._visibilityObserver.observe(this.elementRef.nativeElement);\n });\n }\n\n ngOnDestroy(): void {\n this._visibilityObserver?.disconnect();\n this._visibilityObserver = undefined;\n }\n\n private onBecameVisible(): void {\n if (this._hasBeenVisible) {\n return;\n }\n // One-shot: once loaded we never need to observe again.\n this._visibilityObserver?.disconnect();\n this._visibilityObserver = undefined;\n this.ngZone.run(() => {\n this._hasBeenVisible = true;\n // Flip EffectiveAllowLoad → true, which drives the inner grid's AllowLoad setter to load.\n this.cdr.detectChanges();\n });\n }\n\n /**\n * When true, double-clicking a row emits a Navigate event.\n * Defaults to true.\n */\n @Input() NavigateOnDoubleClick: boolean = true;\n\n // Re-emit events for consumers who need them\n @Output() AfterRowDoubleClick = new EventEmitter<AfterRowDoubleClickEventArgs>();\n @Output() AfterRowClick = new EventEmitter<AfterRowClickEventArgs>();\n @Output() AfterDataLoad = new EventEmitter<AfterDataLoadEventArgs>();\n\n /** Emitted when a row is double-clicked and NavigateOnDoubleClick is true */\n @Output() Navigate = new EventEmitter<FormNavigationEvent>();\n\n onRowDoubleClick(event: AfterRowDoubleClickEventArgs): void {\n // Re-emit the event for any consumers\n this.AfterRowDoubleClick.emit(event);\n\n // Emit navigation event if enabled\n if (this.NavigateOnDoubleClick && event.row) {\n // Use the inner grid's resolved EntityInfo - works for both ViewID and EntityName params\n const entityInfo = this.innerGrid?.EntityInfo;\n if (!entityInfo) return;\n\n const pkey = buildCompositeKey(event.row, entityInfo);\n\n this.Navigate.emit({\n Kind: 'record',\n EntityName: entityInfo.Name,\n PrimaryKey: pkey\n });\n }\n }\n\n onRowClick(event: AfterRowClickEventArgs): void {\n // Re-emit the event for any consumers\n this.AfterRowClick.emit(event);\n }\n\n onDataLoad(event: AfterDataLoadEventArgs): void {\n // Re-emit the event for any consumers\n this.AfterDataLoad.emit(event);\n }\n\n /**\n * The inner grid's \"New\" button bubbles up here. We re-emit as a\n * `new-record` Navigate event so the host form (and Explorer's\n * SingleRecordComponent) can call NavigationService.OpenNewEntityRecord\n * and pre-populate the foreign-key fields from NewRecordValues.\n */\n onNewRecordTabRequested(event: { entityInfo: EntityInfo; defaultValues: Record<string, unknown> }): void {\n this.Navigate.emit({\n Kind: 'new-record',\n EntityName: event.entityInfo.Name,\n DefaultValues: event.defaultValues,\n });\n }\n}\n"]}
1
+ {"version":3,"file":"explorer-entity-data-grid.component.js","sourceRoot":"","sources":["../../src/lib/explorer-entity-data-grid.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, ChangeDetectorRef, NgZone, AfterViewInit, OnDestroy, inject } from '@angular/core';\nimport { RunViewParams } from '@memberjunction/core';\nimport {\n EntityDataGridComponent,\n AfterRowDoubleClickEventArgs,\n AfterRowClickEventArgs,\n AfterDataLoadEventArgs,\n GridToolbarConfig,\n GridSelectionMode,\n buildCompositeKey\n} from '@memberjunction/ng-entity-viewer';\nimport { EntityInfo } from '@memberjunction/core';\nimport { FormNavigationEvent } from './types/navigation-events';\n\n/**\n * Wrapper for EntityDataGridComponent that emits navigation events on row double-click.\n * The host application subscribes to Navigate events and maps them to its routing system.\n *\n * This component is used by CodeGen for related entity grids in forms.\n */\n@Component({\n standalone: false,\n selector: 'mj-explorer-entity-data-grid',\n template: `\n <!-- Related-entity / form-embedded grid: not a saved User View, so there's no canonical\n GridState to manage — suppress the \"Manage Columns\" chooser (AllowColumnToggle=false). -->\n <mj-entity-data-grid\n #innerGrid\n [Params]=\"Params\"\n [NewRecordValues]=\"NewRecordValues\"\n [AllowLoad]=\"EffectiveAllowLoad\"\n [ShowToolbar]=\"ShowToolbar\"\n [Height]=\"Height\"\n [ToolbarConfig]=\"ToolbarConfig\"\n [SelectionMode]=\"SelectionMode\"\n [AllowColumnToggle]=\"false\"\n (AfterRowDoubleClick)=\"onRowDoubleClick($event)\"\n (AfterRowClick)=\"onRowClick($event)\"\n (AfterDataLoad)=\"onDataLoad($event)\"\n (NewRecordTabRequested)=\"onNewRecordTabRequested($event)\">\n </mj-entity-data-grid>\n `,\n styles: [`\n :host {\n display: block;\n height: 100%;\n width: 100%;\n }\n `]\n})\nexport class ExplorerEntityDataGridComponent implements AfterViewInit, OnDestroy {\n @ViewChild('innerGrid') innerGrid!: EntityDataGridComponent;\n\n private elementRef = inject(ElementRef);\n private cdr = inject(ChangeDetectorRef);\n private ngZone = inject(NgZone);\n\n // Pass-through inputs from EntityDataGridComponent\n @Input() Params: RunViewParams | null = null;\n @Input() NewRecordValues: Record<string, unknown> = {};\n @Input() AllowLoad: boolean = true;\n @Input() ShowToolbar: boolean = true;\n @Input() Height: number | 'auto' | 'fit-content' = 'auto';\n @Input() ToolbarConfig: GridToolbarConfig = {};\n @Input() SelectionMode: GridSelectionMode = 'single';\n\n /**\n * When true (default), the inner grid does not fetch until this component's host\n * element first scrolls into the viewport. This prevents related-entity grids on a\n * form — which are always rendered in the DOM (the collapsible panel hides them via\n * CSS, not @if) — from firing a RunView on form open while they're collapsed or below\n * the fold. The first time the panel becomes visible (scrolled into view and expanded,\n * giving the host non-zero area), the grid loads with its normal spinner.\n * Set to false to restore eager loading for consumers that always want immediate data.\n */\n @Input() DeferLoadUntilVisible: boolean = true;\n\n /** True once the host has first intersected the viewport (or deferral is disabled / unsupported). */\n private _hasBeenVisible = false;\n private _visibilityObserver?: IntersectionObserver;\n\n /**\n * Effective AllowLoad passed to the inner grid: the form's AllowLoad AND\n * (deferral disabled OR the panel has been seen at least once).\n */\n get EffectiveAllowLoad(): boolean {\n return this.AllowLoad && (!this.DeferLoadUntilVisible || this._hasBeenVisible);\n }\n\n ngAfterViewInit(): void {\n if (!this.DeferLoadUntilVisible || typeof IntersectionObserver === 'undefined') {\n // Deferral off or unsupported environment — preserve eager-load behavior.\n this._hasBeenVisible = true;\n return;\n }\n\n // Observe outside Angular so scroll churn doesn't trigger change detection;\n // we re-enter the zone only on the one-shot \"became visible\" transition.\n this.ngZone.runOutsideAngular(() => {\n this._visibilityObserver = new IntersectionObserver(\n (entries) => {\n if (entries.some(e => e.isIntersecting)) {\n this.onBecameVisible();\n }\n },\n // Small positive rootMargin pre-loads just before the panel scrolls fully into view.\n { root: null, rootMargin: '200px', threshold: 0 }\n );\n this._visibilityObserver.observe(this.elementRef.nativeElement);\n });\n }\n\n ngOnDestroy(): void {\n this._visibilityObserver?.disconnect();\n this._visibilityObserver = undefined;\n }\n\n private onBecameVisible(): void {\n if (this._hasBeenVisible) {\n return;\n }\n // One-shot: once loaded we never need to observe again.\n this._visibilityObserver?.disconnect();\n this._visibilityObserver = undefined;\n this.ngZone.run(() => {\n this._hasBeenVisible = true;\n // Flip EffectiveAllowLoad → true, which drives the inner grid's AllowLoad setter to load.\n this.cdr.detectChanges();\n });\n }\n\n /**\n * When true, double-clicking a row emits a Navigate event.\n * Defaults to true.\n */\n @Input() NavigateOnDoubleClick: boolean = true;\n\n // Re-emit events for consumers who need them\n @Output() AfterRowDoubleClick = new EventEmitter<AfterRowDoubleClickEventArgs>();\n @Output() AfterRowClick = new EventEmitter<AfterRowClickEventArgs>();\n @Output() AfterDataLoad = new EventEmitter<AfterDataLoadEventArgs>();\n\n /** Emitted when a row is double-clicked and NavigateOnDoubleClick is true */\n @Output() Navigate = new EventEmitter<FormNavigationEvent>();\n\n onRowDoubleClick(event: AfterRowDoubleClickEventArgs): void {\n // Re-emit the event for any consumers\n this.AfterRowDoubleClick.emit(event);\n\n // Emit navigation event if enabled\n if (this.NavigateOnDoubleClick && event.row) {\n // Use the inner grid's resolved EntityInfo - works for both ViewID and EntityName params\n const entityInfo = this.innerGrid?.EntityInfo;\n if (!entityInfo) return;\n\n const pkey = buildCompositeKey(event.row, entityInfo);\n\n this.Navigate.emit({\n Kind: 'record',\n EntityName: entityInfo.Name,\n PrimaryKey: pkey\n });\n }\n }\n\n onRowClick(event: AfterRowClickEventArgs): void {\n // Re-emit the event for any consumers\n this.AfterRowClick.emit(event);\n }\n\n onDataLoad(event: AfterDataLoadEventArgs): void {\n // Re-emit the event for any consumers\n this.AfterDataLoad.emit(event);\n }\n\n /**\n * The inner grid's \"New\" button bubbles up here. We re-emit as a\n * `new-record` Navigate event so the host form (and Explorer's\n * SingleRecordComponent) can call NavigationService.OpenNewEntityRecord\n * and pre-populate the foreign-key fields from NewRecordValues.\n */\n onNewRecordTabRequested(event: { entityInfo: EntityInfo; defaultValues: Record<string, unknown> }): void {\n this.Navigate.emit({\n Kind: 'new-record',\n EntityName: event.entityInfo.Name,\n DefaultValues: event.defaultValues,\n });\n }\n}\n"]}
@@ -6,23 +6,22 @@ import { SimpleEntityFieldInfo } from '@memberjunction/interactive-component-typ
6
6
  import { ReactBridgeService } from '@memberjunction/ng-react';
7
7
  import { BaseFormComponent } from '../base-form-component';
8
8
  import * as i0 from "@angular/core";
9
- import * as i1 from "@memberjunction/ng-react";
10
- import * as i2 from "../container/record-form-container.component";
9
+ import * as i1 from "@memberjunction/ng-ui-components";
10
+ import * as i2 from "@memberjunction/ng-react";
11
+ import * as i3 from "../container/record-form-container.component";
11
12
  const _c0 = ["reactComponent"];
12
13
  function InteractiveFormComponent_Conditional_0_Conditional_0_Conditional_0_Template(rf, ctx) { if (rf & 1) {
13
- i0.ɵɵelementStart(0, "div", 2);
14
- i0.ɵɵelement(1, "i", 5);
15
- i0.ɵɵelementStart(2, "span");
16
- i0.ɵɵtext(3);
17
- i0.ɵɵelementEnd()();
14
+ i0.ɵɵelementStart(0, "mj-alert", 2);
15
+ i0.ɵɵtext(1);
16
+ i0.ɵɵelementEnd();
18
17
  } if (rf & 2) {
19
18
  const ctx_r0 = i0.ɵɵnextContext(3);
20
- i0.ɵɵadvance(3);
19
+ i0.ɵɵadvance();
21
20
  i0.ɵɵtextInterpolate(ctx_r0.loadError);
22
21
  } }
23
22
  function InteractiveFormComponent_Conditional_0_Conditional_0_Conditional_1_Template(rf, ctx) { if (rf & 1) {
24
23
  const _r2 = i0.ɵɵgetCurrentView();
25
- i0.ɵɵelementStart(0, "mj-react-component", 6, 0);
24
+ i0.ɵɵelementStart(0, "mj-react-component", 5, 0);
26
25
  i0.ɵɵlistener("componentEvent", function InteractiveFormComponent_Conditional_0_Conditional_0_Conditional_1_Template_mj_react_component_componentEvent_0_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnReactComponentEvent($event)); })("openEntityRecord", function InteractiveFormComponent_Conditional_0_Conditional_0_Conditional_1_Template_mj_react_component_openEntityRecord_0_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnOpenEntityRecord($event)); });
27
26
  i0.ɵɵelementEnd();
28
27
  } if (rf & 2) {
@@ -31,31 +30,29 @@ function InteractiveFormComponent_Conditional_0_Conditional_0_Conditional_1_Temp
31
30
  } }
32
31
  function InteractiveFormComponent_Conditional_0_Conditional_0_Conditional_2_Template(rf, ctx) { if (rf & 1) {
33
32
  i0.ɵɵelementStart(0, "div", 4);
34
- i0.ɵɵelement(1, "i", 7);
33
+ i0.ɵɵelement(1, "i", 6);
35
34
  i0.ɵɵelementStart(2, "span");
36
35
  i0.ɵɵtext(3, "Loading form\u2026");
37
36
  i0.ɵɵelementEnd()();
38
37
  } }
39
38
  function InteractiveFormComponent_Conditional_0_Conditional_0_Template(rf, ctx) { if (rf & 1) {
40
- i0.ɵɵconditionalCreate(0, InteractiveFormComponent_Conditional_0_Conditional_0_Conditional_0_Template, 4, 1, "div", 2)(1, InteractiveFormComponent_Conditional_0_Conditional_0_Conditional_1_Template, 2, 2, "mj-react-component", 3)(2, InteractiveFormComponent_Conditional_0_Conditional_0_Conditional_2_Template, 4, 0, "div", 4);
39
+ i0.ɵɵconditionalCreate(0, InteractiveFormComponent_Conditional_0_Conditional_0_Conditional_0_Template, 2, 1, "mj-alert", 2)(1, InteractiveFormComponent_Conditional_0_Conditional_0_Conditional_1_Template, 2, 2, "mj-react-component", 3)(2, InteractiveFormComponent_Conditional_0_Conditional_0_Conditional_2_Template, 4, 0, "div", 4);
41
40
  } if (rf & 2) {
42
41
  const ctx_r0 = i0.ɵɵnextContext(2);
43
42
  i0.ɵɵconditional(ctx_r0.loadError ? 0 : ctx_r0.componentSpec && ctx_r0.formHostProps ? 1 : 2);
44
43
  } }
45
44
  function InteractiveFormComponent_Conditional_0_Conditional_1_Conditional_1_Template(rf, ctx) { if (rf & 1) {
46
- i0.ɵɵelementStart(0, "div", 2);
47
- i0.ɵɵelement(1, "i", 5);
48
- i0.ɵɵelementStart(2, "span");
49
- i0.ɵɵtext(3);
50
- i0.ɵɵelementEnd()();
45
+ i0.ɵɵelementStart(0, "mj-alert", 2);
46
+ i0.ɵɵtext(1);
47
+ i0.ɵɵelementEnd();
51
48
  } if (rf & 2) {
52
49
  const ctx_r0 = i0.ɵɵnextContext(3);
53
- i0.ɵɵadvance(3);
50
+ i0.ɵɵadvance();
54
51
  i0.ɵɵtextInterpolate(ctx_r0.loadError);
55
52
  } }
56
53
  function InteractiveFormComponent_Conditional_0_Conditional_1_Conditional_2_Template(rf, ctx) { if (rf & 1) {
57
54
  const _r4 = i0.ɵɵgetCurrentView();
58
- i0.ɵɵelementStart(0, "mj-react-component", 6, 0);
55
+ i0.ɵɵelementStart(0, "mj-react-component", 5, 0);
59
56
  i0.ɵɵlistener("componentEvent", function InteractiveFormComponent_Conditional_0_Conditional_1_Conditional_2_Template_mj_react_component_componentEvent_0_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnReactComponentEvent($event)); })("openEntityRecord", function InteractiveFormComponent_Conditional_0_Conditional_1_Conditional_2_Template_mj_react_component_openEntityRecord_0_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnOpenEntityRecord($event)); });
60
57
  i0.ɵɵelementEnd();
61
58
  } if (rf & 2) {
@@ -64,16 +61,16 @@ function InteractiveFormComponent_Conditional_0_Conditional_1_Conditional_2_Temp
64
61
  } }
65
62
  function InteractiveFormComponent_Conditional_0_Conditional_1_Conditional_3_Template(rf, ctx) { if (rf & 1) {
66
63
  i0.ɵɵelementStart(0, "div", 4);
67
- i0.ɵɵelement(1, "i", 7);
64
+ i0.ɵɵelement(1, "i", 6);
68
65
  i0.ɵɵelementStart(2, "span");
69
66
  i0.ɵɵtext(3, "Loading form\u2026");
70
67
  i0.ɵɵelementEnd()();
71
68
  } }
72
69
  function InteractiveFormComponent_Conditional_0_Conditional_1_Template(rf, ctx) { if (rf & 1) {
73
70
  const _r3 = i0.ɵɵgetCurrentView();
74
- i0.ɵɵelementStart(0, "mj-record-form-container", 8);
71
+ i0.ɵɵelementStart(0, "mj-record-form-container", 7);
75
72
  i0.ɵɵlistener("Navigate", function InteractiveFormComponent_Conditional_0_Conditional_1_Template_mj_record_form_container_Navigate_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.OnFormNavigate($event)); })("DeleteRequested", function InteractiveFormComponent_Conditional_0_Conditional_1_Template_mj_record_form_container_DeleteRequested_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.OnDeleteRequested()); })("FavoriteToggled", function InteractiveFormComponent_Conditional_0_Conditional_1_Template_mj_record_form_container_FavoriteToggled_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.OnFavoriteToggled()); })("HistoryRequested", function InteractiveFormComponent_Conditional_0_Conditional_1_Template_mj_record_form_container_HistoryRequested_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.OnHistoryRequested()); })("ListManagementRequested", function InteractiveFormComponent_Conditional_0_Conditional_1_Template_mj_record_form_container_ListManagementRequested_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.OnListManagementRequested()); });
76
- i0.ɵɵconditionalCreate(1, InteractiveFormComponent_Conditional_0_Conditional_1_Conditional_1_Template, 4, 1, "div", 2)(2, InteractiveFormComponent_Conditional_0_Conditional_1_Conditional_2_Template, 2, 2, "mj-react-component", 3)(3, InteractiveFormComponent_Conditional_0_Conditional_1_Conditional_3_Template, 4, 0, "div", 4);
73
+ i0.ɵɵconditionalCreate(1, InteractiveFormComponent_Conditional_0_Conditional_1_Conditional_1_Template, 2, 1, "mj-alert", 2)(2, InteractiveFormComponent_Conditional_0_Conditional_1_Conditional_2_Template, 2, 2, "mj-react-component", 3)(3, InteractiveFormComponent_Conditional_0_Conditional_1_Conditional_3_Template, 4, 0, "div", 4);
77
74
  i0.ɵɵelementEnd();
78
75
  } if (rf & 2) {
79
76
  const ctx_r0 = i0.ɵɵnextContext(2);
@@ -542,15 +539,15 @@ export class InteractiveFormComponent extends BaseFormComponent {
542
539
  } if (rf & 2) {
543
540
  let _t;
544
541
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.reactComponent = _t.first);
545
- } }, inputs: { record: "record", ComponentID: "ComponentID", componentSpec: "componentSpec", previewMode: "previewMode" }, outputs: { LoadErrorChanged: "LoadErrorChanged" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 1, vars: 1, consts: [["reactComponent", ""], [3, "Record", "FormComponent"], [1, "mj-error-banner"], [3, "component", "componentProps"], [1, "mj-loading-state"], [1, "fa-solid", "fa-triangle-exclamation"], [3, "componentEvent", "openEntityRecord", "component", "componentProps"], [1, "fa-solid", "fa-spinner", "fa-spin"], [3, "Navigate", "DeleteRequested", "FavoriteToggled", "HistoryRequested", "ListManagementRequested", "Record", "FormComponent"]], template: function InteractiveFormComponent_Template(rf, ctx) { if (rf & 1) {
542
+ } }, inputs: { record: "record", ComponentID: "ComponentID", componentSpec: "componentSpec", previewMode: "previewMode" }, outputs: { LoadErrorChanged: "LoadErrorChanged" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 1, vars: 1, consts: [["reactComponent", ""], [3, "Record", "FormComponent"], ["Variant", "error"], [3, "component", "componentProps"], [1, "mj-loading-state"], [3, "componentEvent", "openEntityRecord", "component", "componentProps"], [1, "fa-solid", "fa-spinner", "fa-spin"], [3, "Navigate", "DeleteRequested", "FavoriteToggled", "HistoryRequested", "ListManagementRequested", "Record", "FormComponent"]], template: function InteractiveFormComponent_Template(rf, ctx) { if (rf & 1) {
546
543
  i0.ɵɵconditionalCreate(0, InteractiveFormComponent_Conditional_0_Template, 2, 1);
547
544
  } if (rf & 2) {
548
545
  i0.ɵɵconditional(ctx.record ? 0 : -1);
549
- } }, dependencies: [i1.MJReactComponent, i2.MjRecordFormContainerComponent], encapsulation: 2 });
546
+ } }, dependencies: [i1.MJAlertComponent, i2.MJReactComponent, i3.MjRecordFormContainerComponent], encapsulation: 2 });
550
547
  }
551
548
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(InteractiveFormComponent, [{
552
549
  type: Component,
553
- args: [{ standalone: false, selector: 'mj-interactive-form', template: "@if (record) {\n@if (previewMode) {\n <!-- Preview mode \u2014 no toolbar wrap. Just the form body for clean\n \"what the form looks like\" previews (Form Builder cockpit). -->\n @if (loadError) {\n <div class=\"mj-error-banner\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <span>{{ loadError }}</span>\n </div>\n } @else if (componentSpec && formHostProps) {\n <mj-react-component\n #reactComponent\n [component]=\"componentSpec\"\n [componentProps]=\"formHostProps\"\n (componentEvent)=\"OnReactComponentEvent($event)\"\n (openEntityRecord)=\"OnOpenEntityRecord($event)\">\n </mj-react-component>\n } @else {\n <div class=\"mj-loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading form\u2026</span>\n </div>\n }\n} @else {\n<mj-record-form-container\n [Record]=\"record\"\n [FormComponent]=\"this\"\n (Navigate)=\"OnFormNavigate($event)\"\n (DeleteRequested)=\"OnDeleteRequested()\"\n (FavoriteToggled)=\"OnFavoriteToggled()\"\n (HistoryRequested)=\"OnHistoryRequested()\"\n (ListManagementRequested)=\"OnListManagementRequested()\">\n\n @if (loadError) {\n <div class=\"mj-error-banner\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <span>{{ loadError }}</span>\n </div>\n } @else if (componentSpec && formHostProps) {\n <mj-react-component\n #reactComponent\n [component]=\"componentSpec\"\n [componentProps]=\"formHostProps\"\n (componentEvent)=\"OnReactComponentEvent($event)\"\n (openEntityRecord)=\"OnOpenEntityRecord($event)\">\n </mj-react-component>\n } @else {\n <div class=\"mj-loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading form\u2026</span>\n </div>\n }\n\n</mj-record-form-container>\n}\n}\n" }]
550
+ args: [{ standalone: false, selector: 'mj-interactive-form', template: "@if (record) {\n@if (previewMode) {\n <!-- Preview mode \u2014 no toolbar wrap. Just the form body for clean\n \"what the form looks like\" previews (Form Builder cockpit). -->\n @if (loadError) {\n <mj-alert Variant=\"error\">{{ loadError }}</mj-alert>\n } @else if (componentSpec && formHostProps) {\n <mj-react-component\n #reactComponent\n [component]=\"componentSpec\"\n [componentProps]=\"formHostProps\"\n (componentEvent)=\"OnReactComponentEvent($event)\"\n (openEntityRecord)=\"OnOpenEntityRecord($event)\">\n </mj-react-component>\n } @else {\n <div class=\"mj-loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading form\u2026</span>\n </div>\n }\n} @else {\n<mj-record-form-container\n [Record]=\"record\"\n [FormComponent]=\"this\"\n (Navigate)=\"OnFormNavigate($event)\"\n (DeleteRequested)=\"OnDeleteRequested()\"\n (FavoriteToggled)=\"OnFavoriteToggled()\"\n (HistoryRequested)=\"OnHistoryRequested()\"\n (ListManagementRequested)=\"OnListManagementRequested()\">\n\n @if (loadError) {\n <mj-alert Variant=\"error\">{{ loadError }}</mj-alert>\n } @else if (componentSpec && formHostProps) {\n <mj-react-component\n #reactComponent\n [component]=\"componentSpec\"\n [componentProps]=\"formHostProps\"\n (componentEvent)=\"OnReactComponentEvent($event)\"\n (openEntityRecord)=\"OnOpenEntityRecord($event)\">\n </mj-react-component>\n } @else {\n <div class=\"mj-loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading form\u2026</span>\n </div>\n }\n\n</mj-record-form-container>\n}\n}\n" }]
554
551
  }], null, { record: [{
555
552
  type: Input
556
553
  }], ComponentID: [{
@@ -1 +1 @@
1
- {"version":3,"file":"interactive-form.component.js","sourceRoot":"","sources":["../../../src/lib/interactive-form/interactive-form.component.ts","../../../src/lib/interactive-form/interactive-form.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAqB,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChI,OAAO,EAA4B,QAAQ,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAEvE,OAAO,EAEH,cAAc,EACd,eAAe,EAIf,UAAU,GACb,MAAM,mDAAmD,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACpF,OAAO,EAAoB,kBAAkB,EAAuB,MAAM,0BAA0B,CAAC;AACrG,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;;;;;;ICVnD,8BAA6B;IACzB,uBAAgD;IAChD,4BAAM;IAAA,YAAe;IACzB,AADyB,iBAAO,EAC1B;;;IADI,eAAe;IAAf,sCAAe;;;;IAGzB,gDAKoD;IAAhD,AADA,6PAAkB,oCAA6B,KAAC,oPAC5B,iCAA0B,KAAC;IACnD,iBAAqB;;;IAHjB,AADA,gDAA2B,wCACK;;;IAKpC,8BAA8B;IAC1B,uBAA2C;IAC3C,4BAAM;IAAA,kCAAa;IACvB,AADuB,iBAAO,EACxB;;;IAJR,AARA,AALF,sHAAiB,+GAK4B,gGAQpC;;;IAbT,6FAkBC;;;IAYG,8BAA6B;IACzB,uBAAgD;IAChD,4BAAM;IAAA,YAAe;IACzB,AADyB,iBAAO,EAC1B;;;IADI,eAAe;IAAf,sCAAe;;;;IAGzB,gDAKoD;IAAhD,AADA,6PAAkB,oCAA6B,KAAC,oPAC5B,iCAA0B,KAAC;IACnD,iBAAqB;;;IAHjB,AADA,gDAA2B,wCACK;;;IAKpC,8BAA8B;IAC1B,uBAA2C;IAC3C,4BAAM;IAAA,kCAAa;IACvB,AADuB,iBAAO,EACxB;;;;IA1Bd,mDAO4D;IAAxD,AADA,AADA,AADA,AADA,yOAAY,6BAAsB,KAAC,oOAChB,0BAAmB,KAAC,oOACpB,0BAAmB,KAAC,sOACnB,2BAAoB,KAAC,oPACd,kCAA2B,KAAC;IAerD,AARA,AALF,sHAAiB,+GAK4B,gGAQpC;IAOb,iBAA2B;;;IA3BvB,AADA,sCAAiB,yBACK;IAOtB,cAkBC;IAlBD,6FAkBC;;;IA5BH,AAtBF,8FAAmB,uGAsBV;;;IAtBT,4CAqDC;;ADrCD;;;;;;;;;;;;;;GAcG;AAMH,MAAM,OAAO,wBAAyB,SAAQ,iBAAiB;IAE3D;;;;;;;;;OASG;IACK,OAAO,CAAc;IACrB,kBAAkB,GAAG,KAAK,CAAC;IAEnC,IACoB,MAAM,CAAC,KAAiB;QACxC,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK;YAAE,OAAO;QACnC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,IAAI,IAAI,CAAC,kBAAkB,IAAI,KAAK,EAAE,CAAC;YACnC,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YACvC,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC5B,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC5B,CAAC;IACL,CAAC;IACD,IAAoB,MAAM,KAAiB,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAEjE;;;;OAIG;IACa,WAAW,GAAkB,IAAI,CAAC;IAElD;;;;;;;;;;OAUG;IACa,aAAa,GAAyB,IAAI,CAAC;IAE3D;;;;;;;;;;;OAWG;IACa,WAAW,GAAG,KAAK,CAAC;IAEpC,2FAA2F;IACpF,aAAa,GAAyB,IAAI,CAAC;IAElD,2EAA2E;IACnE,UAAU,GAAkB,IAAI,CAAC;IAEzC;;;;;OAKG;IACH,IAAW,SAAS,KAAoB,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IACjE,IAAW,SAAS,CAAC,CAAgB;QACjC,IAAI,CAAC,KAAK,IAAI,CAAC,UAAU;YAAE,OAAO;QAClC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IAEgB,gBAAgB,GAAG,IAAI,YAAY,EAAiB,CAAC;IAEtE,mFAAmF;IAC3E,gBAAgB,GAA4B,EAAE,CAAC;IAEvD;;;;;;;OAOG;IACiC,cAAc,CAAoB;IAEtE;;;;;OAKG;IACK,mBAAmB,GAAwC,IAAI,CAAC;IAEvD,cAAc,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAC3C,WAAW,GAAG,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAE1C,KAAK,CAAC,QAAQ;QAC1B,MAAM,KAAK,CAAC,QAAQ,EAAE,CAAC;QACvB,sEAAsE;QACtE,oEAAoE;QACpE,kEAAkE;QAClE,+DAA+D;QAC/D,+CAA+C;QAC/C,IAAI,CAAC;YACD,MAAM,IAAI,CAAC,WAAW,CAAC,eAAe,EAAE,CAAC;QAC7C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,QAAQ,CAAC,4DAA4D,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACzH,IAAI,CAAC,SAAS,GAAE,mDAAmD,CAAC;YACpE,OAAO;QACX,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YAC7C,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAChC,CAAC;QACD,mEAAmE;QACnE,+DAA+D;QAC/D,oEAAoE;QACpE,kEAAkE;QAClE,sBAAsB;QACtB,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;QAC/B,qEAAqE;QACrE,0DAA0D;QAC1D,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC1C,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACnC,CAAC;IACL,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,qBAAqB,CAAC,KAA0B;QACzD,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,cAAc,CAAC,UAAU;gBAC1B,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,OAA6B,CAAC,CAAC;gBACjE,MAAM;YACV,KAAK,cAAc,CAAC,YAAY;gBAC5B,MAAM,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,OAA+B,CAAC,CAAC;gBACrE,MAAM;YACV,KAAK,cAAc,CAAC,uBAAuB;gBACvC,IAAI,CAAC,6BAA6B,CAAC,KAAK,CAAC,OAA0C,CAAC,CAAC;gBACrF,MAAM;YACV,8EAA8E;YAC9E,+EAA+E;QACnF,CAAC;IACL,CAAC;IAED;;;;OAIG;IACI,kBAAkB,CAAC,KAAgD;QACtE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;IAChG,CAAC;IAED;;;;;;;;OAQG;IACK,KAAK,CAAC,gBAAgB,CAAC,IAAwB;QACnD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;YAC/B,OAAO;QACX,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACf,QAAQ,CAAC,6DAA6D,CAAC,CAAC;YACxE,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;YAC/B,OAAO;QACX,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;QAC5D,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACnD,oEAAoE;YACpE,sEAAsE;YACtE,sEAAsE;YACtE,mDAAmD;YACnD,OAAO,CAAC,IAAI,CACR,mEAAmE;gBACnE,iEAAiE;gBACjE,kDAAkD,CACrD,CAAC;YACF,oEAAoE;YACpE,qEAAqE;YACrE,oEAAoE;YACpE,8DAA8D;YAC9D,EAAE;YACF,mEAAmE;YACnE,oEAAoE;YACpE,mEAAmE;YACnE,iEAAiE;YACjE,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;YAC9B,OAAO;QACX,CAAC;QACD,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,YAAY,EAAE,CAAC;YAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CACtC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;YACpE,IAAI,KAAK,EAAE,CAAC;gBACR,uDAAuD;gBACvD,2DAA2D;gBAC3D,iCAAiC;gBACjC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACvC,CAAC;iBAAM,CAAC;gBACJ,OAAO,CAAC,IAAI,CACR,6CAA6C,SAAS,mBAAmB;oBACzE,cAAc,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,sBAAsB,CAClE,CAAC;YACN,CAAC;QACL,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACvC,IAAI,KAAK,EAAE,CAAC;YACR,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YAC7C,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAChC,CAAC;aAAM,CAAC;YACJ,QAAQ,CAAC,6CAA6C,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,eAAe,IAAI,eAAe,EAAE,CAAC,CAAC;QAC1J,CAAC;QACD,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAEO,kBAAkB,CAAC,OAAgB;QACvC,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC3B,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC;YAClC,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;QACpC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAAC,IAA0B;QACvD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,OAAO;QACX,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACf,OAAO;QACX,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAC3C,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,QAAQ,CAAC,+CAA+C,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,eAAe,IAAI,eAAe,EAAE,CAAC,CAAC;QAC5J,CAAC;IACL,CAAC;IAEO,6BAA6B,CAAC,IAAqC;QACvE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,OAAO;QACX,CAAC;QACD,IAAI,IAAI,CAAC,aAAa,KAAK,MAAM,EAAE,CAAC;YAChC,IAAI,CAAC,aAAa,EAAE,CAAC;QACzB,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,WAAW,EAAE,CAAC;QACvB,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACa,aAAa;QACzB,KAAK,CAAC,aAAa,EAAE,CAAC;QACtB,IAAI,CAAC,oBAAoB,EAAE,CAAC;IAChC,CAAC;IAEe,WAAW;QACvB,KAAK,CAAC,WAAW,EAAE,CAAC;QACpB,IAAI,CAAC,oBAAoB,EAAE,CAAC;IAChC,CAAC;IAED;;;;;;;;;;;;;OAaG;IACa,KAAK,CAAC,UAAU,CAAC,qBAA8B;QAC3D,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,SAAS,EAAE,CAAC,eAAe,CAAC,WAAW,CAAC,EAAE,CAAC;YACjE,OAAO,KAAK,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC;QACnD,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,OAAO,CAAU,OAAO,CAAC,EAAE;YAC9C,IAAI,CAAC,mBAAmB,GAAG,OAAO,CAAC;QACvC,CAAC,CAAC,CAAC;QACH,IAAI,CAAC;YACD,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;QAClE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;YAChC,QAAQ,CAAC,2DAA2D,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACxH,OAAO,KAAK,CAAC;QACjB,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC;QACjC,IAAI,OAAO,IAAI,qBAAqB,EAAE,CAAC;YACnC,IAAI,CAAC,WAAW,EAAE,CAAC;QACvB,CAAC;QACD,OAAO,OAAO,CAAC;IACnB,CAAC;IAED;;;;;;;;OAQG;IACa,UAAU;QACtB,IAAI,IAAI,CAAC,cAAc,EAAE,SAAS,EAAE,CAAC,eAAe,CAAC,aAAa,CAAC,EAAE,CAAC;YAClE,IAAI,CAAC;gBACD,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;YACpE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACX,QAAQ,CAAC,6DAA6D,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC9H,CAAC;YACD,+DAA+D;YAC/D,8DAA8D;YAC9D,gEAAgE;YAChE,kBAAkB;YAClB,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,OAAO;QACX,CAAC;QACD,KAAK,CAAC,UAAU,EAAE,CAAC;IACvB,CAAC;IAED;;;;;;;;;;;OAWG;IACK,KAAK,CAAC,iBAAiB;QAC3B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACpB,IAAI,CAAC,SAAS,GAAE,sDAAsD,CAAC;YACvE,OAAO;QACX,CAAC;QACD,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC;YACpC,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC;YAClC,gEAAgE;YAChE,IAAI,CAAC;gBACD,MAAM,sBAAsB,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;YACxE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACX,QAAQ,CAAC,mEAAmE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAChI,gEAAgE;YACpE,CAAC;YAED,IAAI,QAAQ,GAAkB,IAAI,CAAC;YACnC,IAAI,YAAgC,CAAC;YAErC,MAAM,MAAM,GAAG,sBAAsB,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC9E,IAAI,MAAM,EAAE,CAAC;gBACT,QAAQ,GAAG,MAAM,CAAC,aAAa,CAAC;gBAChC,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC;YAC/B,CAAC;iBAAM,CAAC;gBACJ,2DAA2D;gBAC3D,yDAAyD;gBACzD,gEAAgE;gBAChE,8DAA8D;gBAC9D,4BAA4B;gBAC5B,MAAM,EAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;gBAClD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAsD;oBACjF,UAAU,EAAE,gBAAgB;oBAC5B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,CAAC;oBACvC,WAAW,EAAE,OAAO,IAAI,CAAC,WAAW,GAAG;oBACvC,OAAO,EAAE,CAAC;oBACV,UAAU,EAAE,QAAQ;oBACpB,WAAW,EAAE,IAAI;iBACpB,EAAE,IAAI,CAAC,CAAC;gBACT,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;oBAC7C,IAAI,CAAC,SAAS,GAAE,aAAa,IAAI,CAAC,WAAW,aAAa,CAAC;oBAC3D,OAAO;gBACX,CAAC;gBACD,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;gBAC3C,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC1C,CAAC;YAED,IAAI,CAAC;gBACD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,MAAM,CAAkB,CAAC;YACzE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACX,IAAI,CAAC,SAAS,GAAE,aAAa,YAAY,IAAI,IAAI,CAAC,WAAW,oCAAoC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBACpJ,OAAO;YACX,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;gBACtB,IAAI,CAAC,SAAS,GAAE,aAAa,YAAY,IAAI,IAAI,CAAC,WAAW,8BAA8B,CAAC;gBAC5F,OAAO;YACX,CAAC;YACD,kEAAkE;YAClE,kEAAkE;YAClE,+CAA+C;YAC/C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;gBAClC,IAAI,CAAC,SAAS,GAAE,aAAa,YAAY,IAAI,IAAI,CAAC,WAAW,yCAAyC,CAAC;gBACvG,OAAO;YACX,CAAC;QACL,CAAC;gBAAS,CAAC;YACP,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,CAAC;QACvC,CAAC;IACL,CAAC;IAED;;;OAGG;IACK,oBAAoB;QACxB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACf,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;YAC1B,OAAO;QACX,CAAC;QACD,MAAM,MAAM,GAA4B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAC/D,qBAAqB,CAAC,mBAAmB,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC;QAElE,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;QAClC,MAAM,UAAU,GAAmC,CAAC,EAAE,IAAI,EAAE,CAAC,QAAQ,CAAC;YAClE,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC;YAC5B,CAAC,CAAC,IAAI,CAAC;QAEX,IAAI,CAAC,aAAa,GAAG;YACjB,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI;YACvC,UAAU;YACV,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;YAC5B,cAAc,EAAE;gBACZ,MAAM;gBACN,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,WAAW,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI;gBAC9E,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,IAAI;aACpD;YACD,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;YACtE,OAAO,EAAE,IAAI,CAAC,WAAW;YACzB,SAAS,EAAE,IAAI,CAAC,aAAa;YAC7B,SAAS,EAAE,IAAI,CAAC,aAAa;SAChC,CAAC;IACN,CAAC;IAEO,iBAAiB,CAAC,EAAgB;QACtC,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,GAAG,IAAI,EAAE,CAAC,aAAa,IAAI,EAAE,EAAE,CAAC;YACvC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC;QACnC,CAAC;QACD,OAAO,GAAG,CAAC;IACf,CAAC;6QA9cQ,wBAAwB,yBAAxB,wBAAwB;6DAAxB,wBAAwB;;;;;;YCrCrC,gFAAc;;YAAd,qCAuDC;;;iFDlBY,wBAAwB;cALpC,SAAS;6BACM,KAAK,YACP,qBAAqB;;kBAkB9B,KAAK;;kBAiBL,KAAK;;kBAaL,KAAK;;kBAcL,KAAK;;kBAqBL,MAAM;;kBAaN,SAAS;mBAAC,gBAAgB;;kFA7FlB,wBAAwB;AAidrC;;;GAGG;AACH,MAAM,UAAU,4BAA4B;IACxC,0CAA0C;IAC1C,IAAI,KAAgB,EAAE,CAAC;QACnB,6DAA6D;QAC7D,MAAM,CAAC,GAAY,wBAAwB,CAAC;IAChD,CAAC;AACL,CAAC","sourcesContent":["import { ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild, inject } from '@angular/core';\nimport { BaseEntity, CompositeKey, LogError, RunView } from '@memberjunction/core';\nimport { InteractiveFormsEngine } from '@memberjunction/core-entities';\nimport { ComponentSpec } from '@memberjunction/interactive-component-types';\nimport {\n FormHostProps,\n FormEventNames,\n FormMethodNames,\n FormBeforeSaveArgs,\n FormBeforeDeleteArgs,\n FormEditModeChangeRequestedArgs,\n isFormRole,\n} from '@memberjunction/interactive-component-types/forms';\nimport { SimpleEntityFieldInfo } from '@memberjunction/interactive-component-types';\nimport { MJReactComponent, ReactBridgeService, ReactComponentEvent } from '@memberjunction/ng-react';\nimport { BaseFormComponent } from '../base-form-component';\n\n/**\n * Wraps a Component (the IC kind — declared `componentRole: 'form'`) so it can\n * stand in for an Angular form on any entity.\n *\n * **Layering invariant:** the React component never touches BaseEntity. This\n * wrapper owns the entity lifecycle (`record.Save`, `record.Delete`, mode\n * transitions). The React component sees only the snapshot in\n * {@link FormHostProps} and communicates back via `BeforeSave` /\n * `BeforeDelete` / `EditModeChangeRequested` events plus its dirty-field diff.\n *\n * Instantiated by `SingleRecordComponent.LoadForm` when the resolver finds an\n * `EntityFormOverride` matching the user's scope. Behaves exactly like the\n * generated Angular form from the host's perspective: same `record` setter,\n * same `EditMode` toggle, same outputs.\n */\n@Component({\n standalone: false,\n selector: 'mj-interactive-form',\n templateUrl: './interactive-form.component.html',\n})\nexport class InteractiveFormComponent extends BaseFormComponent implements OnInit, OnDestroy {\n\n /**\n * The bound record. Backed by a setter so swapping records *after*\n * the component has mounted (e.g. Form Builder cockpit preview record\n * picker) triggers a fresh `originalSnapshot` capture + a\n * `formHostProps` rebuild — without this, the React form keeps\n * rendering the original record's values even after the input\n * changes. Guarded on `_recordInitialized` so the post-mount path\n * doesn't fire during the first input assignment (handled by\n * `ngOnInit`, which sets both `originalSnapshot` and `formHostProps`).\n */\n private _record!: BaseEntity;\n private _recordInitialized = false;\n\n @Input()\n public override set record(value: BaseEntity) {\n if (this._record === value) return;\n this._record = value;\n if (this._recordInitialized && value) {\n this.originalSnapshot = value.GetAll();\n this.rebuildFormHostProps();\n this.cdr.markForCheck();\n }\n }\n public override get record(): BaseEntity { return this._record; }\n\n /**\n * The Component row ID resolved for this (entity, user, roles) tuple. Set\n * by `SingleRecordComponent.LoadForm` after the resolver returns an\n * override.\n */\n @Input() public ComponentID: string | null = null;\n\n /**\n * Loaded ComponentSpec, parsed from the Component row's `Specification` JSON.\n *\n * Two ways to populate this:\n * 1. **Via `ComponentID`** (the standard path): set the input, ngOnInit\n * fetches the row from `MJ: Components` and assigns the parsed spec.\n * 2. **Direct assignment** (the artifact-viewer path): the caller already\n * has the spec in hand (parsed from an artifact JSON blob) and sets\n * `componentSpec` directly. In that case `ComponentID` should be left\n * null and the DB fetch is skipped.\n */\n @Input() public componentSpec: ComponentSpec | null = null;\n\n /**\n * When true, render the React component **without** the\n * `<mj-record-form-container>` wrapper — i.e. no toolbar (no Save /\n * Cancel / Edit / Delete / History / Tags / Lists).\n *\n * Used by the Form Builder cockpit's Preview tab where the toolbar's\n * actions don't apply (Save would save the *record*, but the cockpit's\n * own Save button saves the *spec*; History/Tags assume a real\n * persistent record, but the preview record can be a synthetic\n * NewRecord). Keeping just the form body produces a clean \"this is\n * what the form looks like\" view.\n */\n @Input() public previewMode = false;\n\n /** FormHostProps passed to the React component. Recomputed when record or mode changes. */\n public formHostProps: FormHostProps | null = null;\n\n /** Loaded-spec error (component row missing, JSON parse failure, etc.). */\n private _loadError: string | null = null;\n\n /**\n * Loaded-spec error (component row missing, JSON parse failure,\n * React-runtime bootstrap failure, etc.). Setter auto-emits\n * `LoadErrorChanged` so host surfaces (e.g. Form Builder cockpit\n * Preview tab) can show their own error state — retrospective fix #10.\n */\n public get loadError(): string | null { return this._loadError; }\n public set loadError(v: string | null) {\n if (v === this._loadError) return;\n this._loadError = v;\n this.LoadErrorChanged.emit(v);\n }\n\n @Output() public LoadErrorChanged = new EventEmitter<string | null>();\n\n /** Tracks last-known field snapshot for computing the dirty-field diff on save. */\n private originalSnapshot: Record<string, unknown> = {};\n\n /**\n * Reference to the mounted React component. Used by the SaveRecord /\n * CancelEdit overrides below to invoke the component's registered\n * `RequestSave` / `RequestCancel` methods when the host toolbar fires\n * Save/Cancel — so the single user-visible Save lives on the toolbar\n * (consistent with every other entity form in MJ) and not duplicated\n * inside the form body.\n */\n @ViewChild('reactComponent') public reactComponent?: MJReactComponent;\n\n /**\n * Promise resolver populated when `SaveRecord` invokes `RequestSave`\n * on the React component. `handleBeforeSave` resolves it after the\n * BeforeSave-driven entity save completes, so SaveRecord can return\n * a meaningful success boolean and honor `StopEditModeAfterSave`.\n */\n private pendingSaveResolver: ((success: boolean) => void) | null = null;\n\n private readonly changeDetector = inject(ChangeDetectorRef);\n private readonly reactBridge = inject(ReactBridgeService);\n\n public override async ngOnInit(): Promise<void> {\n await super.ngOnInit();\n // Force the React bridge to be initialized BEFORE we let our template\n // mount the `<mj-react-component>` child. Without this, the child's\n // ngAfterViewInit can race the adapter bootstrap and throw \"React\n // runtime not initialized\" — which then prevents the form from\n // rendering and silently breaks the save flow.\n try {\n await this.reactBridge.getReactContext();\n } catch (err) {\n LogError(`InteractiveFormComponent: React bridge bootstrap failed: ${err instanceof Error ? err.message : String(err)}`);\n this.loadError ='React runtime failed to load. Try a hard refresh.';\n return;\n }\n if (this.record) {\n this.originalSnapshot = this.record.GetAll();\n this.rebuildFormHostProps();\n }\n // Mark init complete so the `record` setter rebuilds on subsequent\n // input changes (post-mount record swaps from the Form Builder\n // cockpit preview picker, etc.). Before this flag flips, the setter\n // intentionally does nothing because ngOnInit already handles the\n // initial assignment.\n this._recordInitialized = true;\n // Direct-spec path (artifact viewer) takes precedence; DB fetch only\n // when no spec was supplied and we have an ID to look up.\n if (!this.componentSpec && this.ComponentID) {\n await this.loadComponentSpec();\n }\n }\n\n /**\n * Called by the React component via `mj-react-component`'s `componentEvent`\n * @Output. Maps the form-role event names to BaseEntity operations.\n */\n public async OnReactComponentEvent(event: ReactComponentEvent): Promise<void> {\n switch (event.type) {\n case FormEventNames.BeforeSave:\n await this.handleBeforeSave(event.payload as FormBeforeSaveArgs);\n break;\n case FormEventNames.BeforeDelete:\n await this.handleBeforeDelete(event.payload as FormBeforeDeleteArgs);\n break;\n case FormEventNames.EditModeChangeRequested:\n this.handleEditModeChangeRequested(event.payload as FormEditModeChangeRequestedArgs);\n break;\n // FieldChanged / DirtyStateChanged / ValidationChanged are read-only signals;\n // the wrapper doesn't need to react beyond what the component does internally.\n }\n }\n\n /**\n * Called when the form-role component drills into a related record via\n * `callbacks.OpenEntityRecord(...)`. Relays it upward as a `Navigate` event\n * (the same path normal FK links use) so the host opens the target record.\n */\n public OnOpenEntityRecord(event: { entityName: string; key: CompositeKey }): void {\n this.Navigate.emit({ Kind: 'record', EntityName: event.entityName, PrimaryKey: event.key });\n }\n\n /**\n * Apply the React component's dirty-field diff to the BaseEntity, save,\n * and refresh the snapshot. Errors surface via `LatestResult.CompleteMessage`\n * per the BaseEntity Save contract.\n *\n * Resolves `pendingSaveResolver` if {@link SaveRecord} initiated this\n * flow via the toolbar — so the toolbar can return a meaningful boolean\n * and honor `StopEditModeAfterSave`.\n */\n private async handleBeforeSave(args: FormBeforeSaveArgs): Promise<void> {\n if (args.cancel) {\n this.resolvePendingSave(false);\n return;\n }\n if (!this.record) {\n LogError('InteractiveFormComponent.handleBeforeSave: no record loaded');\n this.resolvePendingSave(false);\n return;\n }\n\n const dirtyEntries = Object.entries(args.dirtyFields ?? {});\n if (dirtyEntries.length === 0 && this.record.IsSaved) {\n // No dirty fields on an already-persisted record — a genuine no-op.\n // Most likely the user clicked Save without changing anything, or the\n // React draft didn't reach the host before RequestSave fired. Surface\n // it so the user sees why their edits didn't land.\n console.warn(\n '[InteractiveFormComponent] BeforeSave fired with no dirtyFields. ' +\n 'Either the user clicked Save without changing anything, or the ' +\n 'React draft did not propagate before save fired.'\n );\n // Skip Save() + rebuildFormHostProps(): re-rendering the React form\n // would re-fire BeforeSave on a form that emits it on mount/render —\n // an infinite re-mount loop that starves the form's async data load\n // (it unmounts before setState commits). Treat it as a no-op.\n //\n // Guard on IsSaved so this only short-circuits existing records. A\n // NEW record with no dirty fields must still fall through to Save()\n // so the insert is attempted (and any validation failure surfaced)\n // rather than masked as a reported success with no row inserted.\n this.resolvePendingSave(true);\n return;\n }\n for (const [fieldName, value] of dirtyEntries) {\n const field = this.record.Fields.find(f =>\n f.Name.trim().toLowerCase() === fieldName.trim().toLowerCase());\n if (field) {\n // Use the canonical field name from the lookup so case\n // mismatches between the React draft and the entity schema\n // don't silently drop the value.\n this.record.Set(field.Name, value);\n } else {\n console.warn(\n `[InteractiveFormComponent] Unknown field \"${fieldName}\" in dirtyFields ` +\n `for entity ${this.record.EntityInfo.Name}. Value not applied.`\n );\n }\n }\n\n const saved = await this.record.Save();\n if (saved) {\n this.originalSnapshot = this.record.GetAll();\n this.rebuildFormHostProps();\n } else {\n LogError(`InteractiveFormComponent: save failed for ${this.record.EntityInfo.Name}: ${this.record.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n this.resolvePendingSave(saved);\n }\n\n private resolvePendingSave(success: boolean): void {\n if (this.pendingSaveResolver) {\n this.pendingSaveResolver(success);\n this.pendingSaveResolver = null;\n }\n }\n\n private async handleBeforeDelete(args: FormBeforeDeleteArgs): Promise<void> {\n if (args.cancel) {\n return;\n }\n if (!this.record) {\n return;\n }\n const deleted = await this.record.Delete();\n if (!deleted) {\n LogError(`InteractiveFormComponent: delete failed for ${this.record.EntityInfo.Name}: ${this.record.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n }\n\n private handleEditModeChangeRequested(args: FormEditModeChangeRequestedArgs): void {\n if (args.cancel) {\n return;\n }\n if (args.requestedMode === 'edit') {\n this.StartEditMode();\n } else {\n this.EndEditMode();\n }\n }\n\n /**\n * Propagate Angular-toolbar-driven mode changes into the React component's\n * `FormHostProps.mode`. Without these overrides, clicking Edit on the\n * `<mj-record-form-container>` toolbar flips `this.EditMode` but the React\n * side still sees `mode: 'view'`.\n */\n public override StartEditMode(): void {\n super.StartEditMode();\n this.rebuildFormHostProps();\n }\n\n public override EndEditMode(): void {\n super.EndEditMode();\n this.rebuildFormHostProps();\n }\n\n /**\n * Toolbar **Save** override. Routes the click through the React\n * component's registered `RequestSave` method so the React layer can\n * (a) run its own validation, (b) flush its draft state, (c) emit\n * `BeforeSave` with the dirty-field diff — exactly the flow the\n * in-form Save button would trigger. The wrapper then awaits the\n * BeforeSave handler via `pendingSaveResolver`, gets a real\n * success/failure boolean, and honors `StopEditModeAfterSave`.\n *\n * Falls back to the base implementation for components that haven't\n * registered `RequestSave` — preserves correctness for forms that\n * keep their own in-form Save button (older sample, hand-authored\n * forms that pre-date this contract).\n */\n public override async SaveRecord(StopEditModeAfterSave: boolean): Promise<boolean> {\n if (!this.reactComponent?.hasMethod?.(FormMethodNames.RequestSave)) {\n return super.SaveRecord(StopEditModeAfterSave);\n }\n const completion = new Promise<boolean>(resolve => {\n this.pendingSaveResolver = resolve;\n });\n try {\n this.reactComponent.invokeMethod(FormMethodNames.RequestSave);\n } catch (err) {\n this.pendingSaveResolver = null;\n LogError(`InteractiveFormComponent.SaveRecord: RequestSave threw: ${err instanceof Error ? err.message : String(err)}`);\n return false;\n }\n const success = await completion;\n if (success && StopEditModeAfterSave) {\n this.EndEditMode();\n }\n return success;\n }\n\n /**\n * Toolbar **Cancel** override. Asks the React component to discard\n * its draft via `RequestCancel`, then flips out of edit mode the\n * same way the base implementation would. The React component is\n * expected to (a) clear its local draft state and (b) emit\n * `EditModeChangeRequested({ requestedMode: 'view' })` — though we\n * call `EndEditMode` here directly anyway, so even a no-op handler\n * leaves the form in a coherent state.\n */\n public override CancelEdit(): void {\n if (this.reactComponent?.hasMethod?.(FormMethodNames.RequestCancel)) {\n try {\n this.reactComponent.invokeMethod(FormMethodNames.RequestCancel);\n } catch (err) {\n LogError(`InteractiveFormComponent.CancelEdit: RequestCancel threw: ${err instanceof Error ? err.message : String(err)}`);\n }\n // Don't call super.CancelEdit() — base reverts the BaseEntity,\n // but for interactive forms the BaseEntity never received the\n // React draft in the first place, so there's nothing to revert.\n // Just flip mode.\n this.EndEditMode();\n return;\n }\n super.CancelEdit();\n }\n\n /**\n * Resolve the Component row, parse its `Specification`, and surface either\n * the spec or an error. Backed by `InteractiveFormsEngine` (in-memory\n * cache, BaseEntity event-driven invalidation) so this is a sub-ms JS\n * lookup once the engine has loaded — the engine handles freshness\n * via remote-invalidate events, replacing the prior RunView's\n * `BypassCache: true` escape hatch. Falls through to a one-shot\n * RunView only on engine-cache miss (defensive — the engine should\n * always have a Type='Form' Component, but if a brand-new Component\n * was just created on the server and the invalidation event hasn't\n * propagated yet, we still want to render rather than blank-screen).\n */\n private async loadComponentSpec(): Promise<void> {\n if (!this.ComponentID) {\n this.loadError ='No ComponentID provided to InteractiveFormComponent.';\n return;\n }\n try {\n const provider = this.ProviderToUse;\n const user = provider.CurrentUser;\n // Lazy-load the form-metadata cache. No-op when already loaded.\n try {\n await InteractiveFormsEngine.Instance.Config(false, user, provider);\n } catch (err) {\n LogError(`InteractiveFormComponent: InteractiveFormsEngine.Config failed: ${err instanceof Error ? err.message : String(err)}`);\n // Fall through to RunView path below — engine miss isn't fatal.\n }\n\n let specJson: string | null = null;\n let nameForError: string | undefined;\n\n const cached = InteractiveFormsEngine.Instance.FindFormByID(this.ComponentID);\n if (cached) {\n specJson = cached.Specification;\n nameForError = cached.Name;\n } else {\n // Engine miss — one-shot RunView fallback. This covers the\n // narrow window where a freshly-created Component hasn't\n // propagated to the engine yet (cross-process remote-invalidate\n // is debounced ~1.5s). After the propagation lands subsequent\n // form opens hit the cache.\n const rv = RunView.FromMetadataProvider(provider);\n const result = await rv.RunView<{ ID: string; Specification: string; Name: string }>({\n EntityName: 'MJ: Components',\n Fields: ['ID', 'Name', 'Specification'],\n ExtraFilter: `ID='${this.ComponentID}'`,\n MaxRows: 1,\n ResultType: 'simple',\n BypassCache: true,\n }, user);\n if (!result.Success || !result.Results?.length) {\n this.loadError =`Component ${this.ComponentID} not found.`;\n return;\n }\n specJson = result.Results[0].Specification;\n nameForError = result.Results[0].Name;\n }\n\n try {\n this.componentSpec = JSON.parse(specJson ?? 'null') as ComponentSpec;\n } catch (err) {\n this.loadError =`Component ${nameForError ?? this.ComponentID} has invalid Specification JSON: ${err instanceof Error ? err.message : String(err)}`;\n return;\n }\n if (!this.componentSpec) {\n this.loadError =`Component ${nameForError ?? this.ComponentID} has an empty Specification.`;\n return;\n }\n // Validate the spec commits to the form-role contract. We use the\n // helper (whose signature is structural via Pick<>) so this stays\n // robust across ComponentSpec field additions.\n if (!isFormRole(this.componentSpec)) {\n this.loadError =`Component ${nameForError ?? this.ComponentID} does not declare componentRole='form'.`;\n return;\n }\n } finally {\n this.changeDetector.markForCheck();\n }\n }\n\n /**\n * Build `FormHostProps` from the current record + permissions + edit mode.\n * Called whenever the record loads, mode changes, or save completes.\n */\n private rebuildFormHostProps(): void {\n if (!this.record) {\n this.formHostProps = null;\n return;\n }\n const fields: SimpleEntityFieldInfo[] = this.record.Fields.map(f =>\n SimpleEntityFieldInfo.FromEntityFieldInfo(f.EntityFieldInfo));\n\n const pk = this.record.PrimaryKey;\n const primaryKey: Record<string, unknown> | null = (pk && pk.HasValue)\n ? this.primaryKeyToPlain(pk)\n : null;\n\n this.formHostProps = {\n entityName: this.record.EntityInfo.Name,\n primaryKey,\n record: this.record.GetAll(),\n entityMetadata: {\n fields,\n displayName: this.record.EntityInfo.DisplayName ?? this.record.EntityInfo.Name,\n nameField: this.record.EntityInfo.NameField?.Name,\n },\n mode: this.EditMode ? 'edit' : (pk && pk.HasValue ? 'view' : 'create'),\n canEdit: this.UserCanEdit,\n canDelete: this.UserCanDelete,\n canCreate: this.UserCanCreate,\n };\n }\n\n private primaryKeyToPlain(pk: CompositeKey): Record<string, unknown> {\n const obj: Record<string, unknown> = {};\n for (const kvp of pk.KeyValuePairs ?? []) {\n obj[kvp.FieldName] = kvp.Value;\n }\n return obj;\n }\n}\n\n/**\n * Tree-shaking guard. Imported and called from public-api.ts so bundlers don't\n * drop the component when no consumer references it by name.\n */\nexport function LoadInteractiveFormComponent(): void {\n // Force the class reference to stay live.\n if (false as boolean) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const _: unknown = InteractiveFormComponent;\n }\n}\n","@if (record) {\n@if (previewMode) {\n <!-- Preview mode — no toolbar wrap. Just the form body for clean\n \"what the form looks like\" previews (Form Builder cockpit). -->\n @if (loadError) {\n <div class=\"mj-error-banner\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <span>{{ loadError }}</span>\n </div>\n } @else if (componentSpec && formHostProps) {\n <mj-react-component\n #reactComponent\n [component]=\"componentSpec\"\n [componentProps]=\"formHostProps\"\n (componentEvent)=\"OnReactComponentEvent($event)\"\n (openEntityRecord)=\"OnOpenEntityRecord($event)\">\n </mj-react-component>\n } @else {\n <div class=\"mj-loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading form…</span>\n </div>\n }\n} @else {\n<mj-record-form-container\n [Record]=\"record\"\n [FormComponent]=\"this\"\n (Navigate)=\"OnFormNavigate($event)\"\n (DeleteRequested)=\"OnDeleteRequested()\"\n (FavoriteToggled)=\"OnFavoriteToggled()\"\n (HistoryRequested)=\"OnHistoryRequested()\"\n (ListManagementRequested)=\"OnListManagementRequested()\">\n\n @if (loadError) {\n <div class=\"mj-error-banner\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <span>{{ loadError }}</span>\n </div>\n } @else if (componentSpec && formHostProps) {\n <mj-react-component\n #reactComponent\n [component]=\"componentSpec\"\n [componentProps]=\"formHostProps\"\n (componentEvent)=\"OnReactComponentEvent($event)\"\n (openEntityRecord)=\"OnOpenEntityRecord($event)\">\n </mj-react-component>\n } @else {\n <div class=\"mj-loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading form…</span>\n </div>\n }\n\n</mj-record-form-container>\n}\n}\n"]}
1
+ {"version":3,"file":"interactive-form.component.js","sourceRoot":"","sources":["../../../src/lib/interactive-form/interactive-form.component.ts","../../../src/lib/interactive-form/interactive-form.component.html"],"names":[],"mappings":"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","sourcesContent":["import { ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild, inject } from '@angular/core';\nimport { BaseEntity, CompositeKey, LogError, RunView } from '@memberjunction/core';\nimport { InteractiveFormsEngine } from '@memberjunction/core-entities';\nimport { ComponentSpec } from '@memberjunction/interactive-component-types';\nimport {\n FormHostProps,\n FormEventNames,\n FormMethodNames,\n FormBeforeSaveArgs,\n FormBeforeDeleteArgs,\n FormEditModeChangeRequestedArgs,\n isFormRole,\n} from '@memberjunction/interactive-component-types/forms';\nimport { SimpleEntityFieldInfo } from '@memberjunction/interactive-component-types';\nimport { MJReactComponent, ReactBridgeService, ReactComponentEvent } from '@memberjunction/ng-react';\nimport { BaseFormComponent } from '../base-form-component';\n\n/**\n * Wraps a Component (the IC kind — declared `componentRole: 'form'`) so it can\n * stand in for an Angular form on any entity.\n *\n * **Layering invariant:** the React component never touches BaseEntity. This\n * wrapper owns the entity lifecycle (`record.Save`, `record.Delete`, mode\n * transitions). The React component sees only the snapshot in\n * {@link FormHostProps} and communicates back via `BeforeSave` /\n * `BeforeDelete` / `EditModeChangeRequested` events plus its dirty-field diff.\n *\n * Instantiated by `SingleRecordComponent.LoadForm` when the resolver finds an\n * `EntityFormOverride` matching the user's scope. Behaves exactly like the\n * generated Angular form from the host's perspective: same `record` setter,\n * same `EditMode` toggle, same outputs.\n */\n@Component({\n standalone: false,\n selector: 'mj-interactive-form',\n templateUrl: './interactive-form.component.html',\n})\nexport class InteractiveFormComponent extends BaseFormComponent implements OnInit, OnDestroy {\n\n /**\n * The bound record. Backed by a setter so swapping records *after*\n * the component has mounted (e.g. Form Builder cockpit preview record\n * picker) triggers a fresh `originalSnapshot` capture + a\n * `formHostProps` rebuild — without this, the React form keeps\n * rendering the original record's values even after the input\n * changes. Guarded on `_recordInitialized` so the post-mount path\n * doesn't fire during the first input assignment (handled by\n * `ngOnInit`, which sets both `originalSnapshot` and `formHostProps`).\n */\n private _record!: BaseEntity;\n private _recordInitialized = false;\n\n @Input()\n public override set record(value: BaseEntity) {\n if (this._record === value) return;\n this._record = value;\n if (this._recordInitialized && value) {\n this.originalSnapshot = value.GetAll();\n this.rebuildFormHostProps();\n this.cdr.markForCheck();\n }\n }\n public override get record(): BaseEntity { return this._record; }\n\n /**\n * The Component row ID resolved for this (entity, user, roles) tuple. Set\n * by `SingleRecordComponent.LoadForm` after the resolver returns an\n * override.\n */\n @Input() public ComponentID: string | null = null;\n\n /**\n * Loaded ComponentSpec, parsed from the Component row's `Specification` JSON.\n *\n * Two ways to populate this:\n * 1. **Via `ComponentID`** (the standard path): set the input, ngOnInit\n * fetches the row from `MJ: Components` and assigns the parsed spec.\n * 2. **Direct assignment** (the artifact-viewer path): the caller already\n * has the spec in hand (parsed from an artifact JSON blob) and sets\n * `componentSpec` directly. In that case `ComponentID` should be left\n * null and the DB fetch is skipped.\n */\n @Input() public componentSpec: ComponentSpec | null = null;\n\n /**\n * When true, render the React component **without** the\n * `<mj-record-form-container>` wrapper — i.e. no toolbar (no Save /\n * Cancel / Edit / Delete / History / Tags / Lists).\n *\n * Used by the Form Builder cockpit's Preview tab where the toolbar's\n * actions don't apply (Save would save the *record*, but the cockpit's\n * own Save button saves the *spec*; History/Tags assume a real\n * persistent record, but the preview record can be a synthetic\n * NewRecord). Keeping just the form body produces a clean \"this is\n * what the form looks like\" view.\n */\n @Input() public previewMode = false;\n\n /** FormHostProps passed to the React component. Recomputed when record or mode changes. */\n public formHostProps: FormHostProps | null = null;\n\n /** Loaded-spec error (component row missing, JSON parse failure, etc.). */\n private _loadError: string | null = null;\n\n /**\n * Loaded-spec error (component row missing, JSON parse failure,\n * React-runtime bootstrap failure, etc.). Setter auto-emits\n * `LoadErrorChanged` so host surfaces (e.g. Form Builder cockpit\n * Preview tab) can show their own error state — retrospective fix #10.\n */\n public get loadError(): string | null { return this._loadError; }\n public set loadError(v: string | null) {\n if (v === this._loadError) return;\n this._loadError = v;\n this.LoadErrorChanged.emit(v);\n }\n\n @Output() public LoadErrorChanged = new EventEmitter<string | null>();\n\n /** Tracks last-known field snapshot for computing the dirty-field diff on save. */\n private originalSnapshot: Record<string, unknown> = {};\n\n /**\n * Reference to the mounted React component. Used by the SaveRecord /\n * CancelEdit overrides below to invoke the component's registered\n * `RequestSave` / `RequestCancel` methods when the host toolbar fires\n * Save/Cancel — so the single user-visible Save lives on the toolbar\n * (consistent with every other entity form in MJ) and not duplicated\n * inside the form body.\n */\n @ViewChild('reactComponent') public reactComponent?: MJReactComponent;\n\n /**\n * Promise resolver populated when `SaveRecord` invokes `RequestSave`\n * on the React component. `handleBeforeSave` resolves it after the\n * BeforeSave-driven entity save completes, so SaveRecord can return\n * a meaningful success boolean and honor `StopEditModeAfterSave`.\n */\n private pendingSaveResolver: ((success: boolean) => void) | null = null;\n\n private readonly changeDetector = inject(ChangeDetectorRef);\n private readonly reactBridge = inject(ReactBridgeService);\n\n public override async ngOnInit(): Promise<void> {\n await super.ngOnInit();\n // Force the React bridge to be initialized BEFORE we let our template\n // mount the `<mj-react-component>` child. Without this, the child's\n // ngAfterViewInit can race the adapter bootstrap and throw \"React\n // runtime not initialized\" — which then prevents the form from\n // rendering and silently breaks the save flow.\n try {\n await this.reactBridge.getReactContext();\n } catch (err) {\n LogError(`InteractiveFormComponent: React bridge bootstrap failed: ${err instanceof Error ? err.message : String(err)}`);\n this.loadError ='React runtime failed to load. Try a hard refresh.';\n return;\n }\n if (this.record) {\n this.originalSnapshot = this.record.GetAll();\n this.rebuildFormHostProps();\n }\n // Mark init complete so the `record` setter rebuilds on subsequent\n // input changes (post-mount record swaps from the Form Builder\n // cockpit preview picker, etc.). Before this flag flips, the setter\n // intentionally does nothing because ngOnInit already handles the\n // initial assignment.\n this._recordInitialized = true;\n // Direct-spec path (artifact viewer) takes precedence; DB fetch only\n // when no spec was supplied and we have an ID to look up.\n if (!this.componentSpec && this.ComponentID) {\n await this.loadComponentSpec();\n }\n }\n\n /**\n * Called by the React component via `mj-react-component`'s `componentEvent`\n * @Output. Maps the form-role event names to BaseEntity operations.\n */\n public async OnReactComponentEvent(event: ReactComponentEvent): Promise<void> {\n switch (event.type) {\n case FormEventNames.BeforeSave:\n await this.handleBeforeSave(event.payload as FormBeforeSaveArgs);\n break;\n case FormEventNames.BeforeDelete:\n await this.handleBeforeDelete(event.payload as FormBeforeDeleteArgs);\n break;\n case FormEventNames.EditModeChangeRequested:\n this.handleEditModeChangeRequested(event.payload as FormEditModeChangeRequestedArgs);\n break;\n // FieldChanged / DirtyStateChanged / ValidationChanged are read-only signals;\n // the wrapper doesn't need to react beyond what the component does internally.\n }\n }\n\n /**\n * Called when the form-role component drills into a related record via\n * `callbacks.OpenEntityRecord(...)`. Relays it upward as a `Navigate` event\n * (the same path normal FK links use) so the host opens the target record.\n */\n public OnOpenEntityRecord(event: { entityName: string; key: CompositeKey }): void {\n this.Navigate.emit({ Kind: 'record', EntityName: event.entityName, PrimaryKey: event.key });\n }\n\n /**\n * Apply the React component's dirty-field diff to the BaseEntity, save,\n * and refresh the snapshot. Errors surface via `LatestResult.CompleteMessage`\n * per the BaseEntity Save contract.\n *\n * Resolves `pendingSaveResolver` if {@link SaveRecord} initiated this\n * flow via the toolbar — so the toolbar can return a meaningful boolean\n * and honor `StopEditModeAfterSave`.\n */\n private async handleBeforeSave(args: FormBeforeSaveArgs): Promise<void> {\n if (args.cancel) {\n this.resolvePendingSave(false);\n return;\n }\n if (!this.record) {\n LogError('InteractiveFormComponent.handleBeforeSave: no record loaded');\n this.resolvePendingSave(false);\n return;\n }\n\n const dirtyEntries = Object.entries(args.dirtyFields ?? {});\n if (dirtyEntries.length === 0 && this.record.IsSaved) {\n // No dirty fields on an already-persisted record — a genuine no-op.\n // Most likely the user clicked Save without changing anything, or the\n // React draft didn't reach the host before RequestSave fired. Surface\n // it so the user sees why their edits didn't land.\n console.warn(\n '[InteractiveFormComponent] BeforeSave fired with no dirtyFields. ' +\n 'Either the user clicked Save without changing anything, or the ' +\n 'React draft did not propagate before save fired.'\n );\n // Skip Save() + rebuildFormHostProps(): re-rendering the React form\n // would re-fire BeforeSave on a form that emits it on mount/render —\n // an infinite re-mount loop that starves the form's async data load\n // (it unmounts before setState commits). Treat it as a no-op.\n //\n // Guard on IsSaved so this only short-circuits existing records. A\n // NEW record with no dirty fields must still fall through to Save()\n // so the insert is attempted (and any validation failure surfaced)\n // rather than masked as a reported success with no row inserted.\n this.resolvePendingSave(true);\n return;\n }\n for (const [fieldName, value] of dirtyEntries) {\n const field = this.record.Fields.find(f =>\n f.Name.trim().toLowerCase() === fieldName.trim().toLowerCase());\n if (field) {\n // Use the canonical field name from the lookup so case\n // mismatches between the React draft and the entity schema\n // don't silently drop the value.\n this.record.Set(field.Name, value);\n } else {\n console.warn(\n `[InteractiveFormComponent] Unknown field \"${fieldName}\" in dirtyFields ` +\n `for entity ${this.record.EntityInfo.Name}. Value not applied.`\n );\n }\n }\n\n const saved = await this.record.Save();\n if (saved) {\n this.originalSnapshot = this.record.GetAll();\n this.rebuildFormHostProps();\n } else {\n LogError(`InteractiveFormComponent: save failed for ${this.record.EntityInfo.Name}: ${this.record.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n this.resolvePendingSave(saved);\n }\n\n private resolvePendingSave(success: boolean): void {\n if (this.pendingSaveResolver) {\n this.pendingSaveResolver(success);\n this.pendingSaveResolver = null;\n }\n }\n\n private async handleBeforeDelete(args: FormBeforeDeleteArgs): Promise<void> {\n if (args.cancel) {\n return;\n }\n if (!this.record) {\n return;\n }\n const deleted = await this.record.Delete();\n if (!deleted) {\n LogError(`InteractiveFormComponent: delete failed for ${this.record.EntityInfo.Name}: ${this.record.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n }\n\n private handleEditModeChangeRequested(args: FormEditModeChangeRequestedArgs): void {\n if (args.cancel) {\n return;\n }\n if (args.requestedMode === 'edit') {\n this.StartEditMode();\n } else {\n this.EndEditMode();\n }\n }\n\n /**\n * Propagate Angular-toolbar-driven mode changes into the React component's\n * `FormHostProps.mode`. Without these overrides, clicking Edit on the\n * `<mj-record-form-container>` toolbar flips `this.EditMode` but the React\n * side still sees `mode: 'view'`.\n */\n public override StartEditMode(): void {\n super.StartEditMode();\n this.rebuildFormHostProps();\n }\n\n public override EndEditMode(): void {\n super.EndEditMode();\n this.rebuildFormHostProps();\n }\n\n /**\n * Toolbar **Save** override. Routes the click through the React\n * component's registered `RequestSave` method so the React layer can\n * (a) run its own validation, (b) flush its draft state, (c) emit\n * `BeforeSave` with the dirty-field diff — exactly the flow the\n * in-form Save button would trigger. The wrapper then awaits the\n * BeforeSave handler via `pendingSaveResolver`, gets a real\n * success/failure boolean, and honors `StopEditModeAfterSave`.\n *\n * Falls back to the base implementation for components that haven't\n * registered `RequestSave` — preserves correctness for forms that\n * keep their own in-form Save button (older sample, hand-authored\n * forms that pre-date this contract).\n */\n public override async SaveRecord(StopEditModeAfterSave: boolean): Promise<boolean> {\n if (!this.reactComponent?.hasMethod?.(FormMethodNames.RequestSave)) {\n return super.SaveRecord(StopEditModeAfterSave);\n }\n const completion = new Promise<boolean>(resolve => {\n this.pendingSaveResolver = resolve;\n });\n try {\n this.reactComponent.invokeMethod(FormMethodNames.RequestSave);\n } catch (err) {\n this.pendingSaveResolver = null;\n LogError(`InteractiveFormComponent.SaveRecord: RequestSave threw: ${err instanceof Error ? err.message : String(err)}`);\n return false;\n }\n const success = await completion;\n if (success && StopEditModeAfterSave) {\n this.EndEditMode();\n }\n return success;\n }\n\n /**\n * Toolbar **Cancel** override. Asks the React component to discard\n * its draft via `RequestCancel`, then flips out of edit mode the\n * same way the base implementation would. The React component is\n * expected to (a) clear its local draft state and (b) emit\n * `EditModeChangeRequested({ requestedMode: 'view' })` — though we\n * call `EndEditMode` here directly anyway, so even a no-op handler\n * leaves the form in a coherent state.\n */\n public override CancelEdit(): void {\n if (this.reactComponent?.hasMethod?.(FormMethodNames.RequestCancel)) {\n try {\n this.reactComponent.invokeMethod(FormMethodNames.RequestCancel);\n } catch (err) {\n LogError(`InteractiveFormComponent.CancelEdit: RequestCancel threw: ${err instanceof Error ? err.message : String(err)}`);\n }\n // Don't call super.CancelEdit() — base reverts the BaseEntity,\n // but for interactive forms the BaseEntity never received the\n // React draft in the first place, so there's nothing to revert.\n // Just flip mode.\n this.EndEditMode();\n return;\n }\n super.CancelEdit();\n }\n\n /**\n * Resolve the Component row, parse its `Specification`, and surface either\n * the spec or an error. Backed by `InteractiveFormsEngine` (in-memory\n * cache, BaseEntity event-driven invalidation) so this is a sub-ms JS\n * lookup once the engine has loaded — the engine handles freshness\n * via remote-invalidate events, replacing the prior RunView's\n * `BypassCache: true` escape hatch. Falls through to a one-shot\n * RunView only on engine-cache miss (defensive — the engine should\n * always have a Type='Form' Component, but if a brand-new Component\n * was just created on the server and the invalidation event hasn't\n * propagated yet, we still want to render rather than blank-screen).\n */\n private async loadComponentSpec(): Promise<void> {\n if (!this.ComponentID) {\n this.loadError ='No ComponentID provided to InteractiveFormComponent.';\n return;\n }\n try {\n const provider = this.ProviderToUse;\n const user = provider.CurrentUser;\n // Lazy-load the form-metadata cache. No-op when already loaded.\n try {\n await InteractiveFormsEngine.Instance.Config(false, user, provider);\n } catch (err) {\n LogError(`InteractiveFormComponent: InteractiveFormsEngine.Config failed: ${err instanceof Error ? err.message : String(err)}`);\n // Fall through to RunView path below — engine miss isn't fatal.\n }\n\n let specJson: string | null = null;\n let nameForError: string | undefined;\n\n const cached = InteractiveFormsEngine.Instance.FindFormByID(this.ComponentID);\n if (cached) {\n specJson = cached.Specification;\n nameForError = cached.Name;\n } else {\n // Engine miss — one-shot RunView fallback. This covers the\n // narrow window where a freshly-created Component hasn't\n // propagated to the engine yet (cross-process remote-invalidate\n // is debounced ~1.5s). After the propagation lands subsequent\n // form opens hit the cache.\n const rv = RunView.FromMetadataProvider(provider);\n const result = await rv.RunView<{ ID: string; Specification: string; Name: string }>({\n EntityName: 'MJ: Components',\n Fields: ['ID', 'Name', 'Specification'],\n ExtraFilter: `ID='${this.ComponentID}'`,\n MaxRows: 1,\n ResultType: 'simple',\n BypassCache: true,\n }, user);\n if (!result.Success || !result.Results?.length) {\n this.loadError =`Component ${this.ComponentID} not found.`;\n return;\n }\n specJson = result.Results[0].Specification;\n nameForError = result.Results[0].Name;\n }\n\n try {\n this.componentSpec = JSON.parse(specJson ?? 'null') as ComponentSpec;\n } catch (err) {\n this.loadError =`Component ${nameForError ?? this.ComponentID} has invalid Specification JSON: ${err instanceof Error ? err.message : String(err)}`;\n return;\n }\n if (!this.componentSpec) {\n this.loadError =`Component ${nameForError ?? this.ComponentID} has an empty Specification.`;\n return;\n }\n // Validate the spec commits to the form-role contract. We use the\n // helper (whose signature is structural via Pick<>) so this stays\n // robust across ComponentSpec field additions.\n if (!isFormRole(this.componentSpec)) {\n this.loadError =`Component ${nameForError ?? this.ComponentID} does not declare componentRole='form'.`;\n return;\n }\n } finally {\n this.changeDetector.markForCheck();\n }\n }\n\n /**\n * Build `FormHostProps` from the current record + permissions + edit mode.\n * Called whenever the record loads, mode changes, or save completes.\n */\n private rebuildFormHostProps(): void {\n if (!this.record) {\n this.formHostProps = null;\n return;\n }\n const fields: SimpleEntityFieldInfo[] = this.record.Fields.map(f =>\n SimpleEntityFieldInfo.FromEntityFieldInfo(f.EntityFieldInfo));\n\n const pk = this.record.PrimaryKey;\n const primaryKey: Record<string, unknown> | null = (pk && pk.HasValue)\n ? this.primaryKeyToPlain(pk)\n : null;\n\n this.formHostProps = {\n entityName: this.record.EntityInfo.Name,\n primaryKey,\n record: this.record.GetAll(),\n entityMetadata: {\n fields,\n displayName: this.record.EntityInfo.DisplayName ?? this.record.EntityInfo.Name,\n nameField: this.record.EntityInfo.NameField?.Name,\n },\n mode: this.EditMode ? 'edit' : (pk && pk.HasValue ? 'view' : 'create'),\n canEdit: this.UserCanEdit,\n canDelete: this.UserCanDelete,\n canCreate: this.UserCanCreate,\n };\n }\n\n private primaryKeyToPlain(pk: CompositeKey): Record<string, unknown> {\n const obj: Record<string, unknown> = {};\n for (const kvp of pk.KeyValuePairs ?? []) {\n obj[kvp.FieldName] = kvp.Value;\n }\n return obj;\n }\n}\n\n/**\n * Tree-shaking guard. Imported and called from public-api.ts so bundlers don't\n * drop the component when no consumer references it by name.\n */\nexport function LoadInteractiveFormComponent(): void {\n // Force the class reference to stay live.\n if (false as boolean) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const _: unknown = InteractiveFormComponent;\n }\n}\n","@if (record) {\n@if (previewMode) {\n <!-- Preview mode — no toolbar wrap. Just the form body for clean\n \"what the form looks like\" previews (Form Builder cockpit). -->\n @if (loadError) {\n <mj-alert Variant=\"error\">{{ loadError }}</mj-alert>\n } @else if (componentSpec && formHostProps) {\n <mj-react-component\n #reactComponent\n [component]=\"componentSpec\"\n [componentProps]=\"formHostProps\"\n (componentEvent)=\"OnReactComponentEvent($event)\"\n (openEntityRecord)=\"OnOpenEntityRecord($event)\">\n </mj-react-component>\n } @else {\n <div class=\"mj-loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading form…</span>\n </div>\n }\n} @else {\n<mj-record-form-container\n [Record]=\"record\"\n [FormComponent]=\"this\"\n (Navigate)=\"OnFormNavigate($event)\"\n (DeleteRequested)=\"OnDeleteRequested()\"\n (FavoriteToggled)=\"OnFavoriteToggled()\"\n (HistoryRequested)=\"OnHistoryRequested()\"\n (ListManagementRequested)=\"OnListManagementRequested()\">\n\n @if (loadError) {\n <mj-alert Variant=\"error\">{{ loadError }}</mj-alert>\n } @else if (componentSpec && formHostProps) {\n <mj-react-component\n #reactComponent\n [component]=\"componentSpec\"\n [componentProps]=\"formHostProps\"\n (componentEvent)=\"OnReactComponentEvent($event)\"\n (openEntityRecord)=\"OnOpenEntityRecord($event)\">\n </mj-react-component>\n } @else {\n <div class=\"mj-loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading form…</span>\n </div>\n }\n\n</mj-record-form-container>\n}\n}\n"]}
@@ -38,9 +38,12 @@ export type FormPanelSlot = 'top-area' | 'before-fields' | 'after-fields' | 'aft
38
38
  * `<mj-form-panel-slot>` host filters registrations by matching against this
39
39
  * shape — registrations without it are silently ignored.
40
40
  *
41
- * - `entity` — the `MJContentSourceEntity_IContentSourceConfiguration`-style
42
- * entity name. Must match the entity-form's record entity exactly
43
- * (case-sensitive trim-equality enforced by the slot host).
41
+ * - `entity` — the exact entity name (case-sensitive equality enforced by the
42
+ * slot host), OR the wildcard `'*'` to register an
43
+ * **entity-agnostic** panel that mounts on EVERY entity's form.
44
+ * Wildcard panels are expected to self-hide (render nothing) when
45
+ * they don't apply to the current record, so the cross-cutting
46
+ * registration stays unobtrusive on forms it has no business on.
44
47
  * - `slot` — which slot the panel renders into.
45
48
  * - `sortKey`— higher = earlier within the slot. Defaults to 0 if omitted.
46
49
  * Use ranges (e.g., 100/50/10) so future panels can wedge in
@@ -1 +1 @@
1
- {"version":3,"file":"base-form-panel.d.ts","sourceRoot":"","sources":["../../../src/lib/panel-slot/base-form-panel.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,MAAM,aAAa,GACnB,UAAU,GACV,eAAe,GACf,cAAc,GACd,eAAe,GACf,kBAAkB,CAAC;AAEzB;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,6BAA8B,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,8BACsB,aAAa,CAAC,OAAO,SAAS,UAAU,GAAG,UAAU;IACvE,6EAA6E;IACpE,MAAM,EAAG,OAAO,CAAC;IAC1B,kGAAkG;IACzF,aAAa,EAAG,iBAAiB,CAAC;IAC3C,qGAAqG;IAC5F,WAAW,CAAC,EAAE,WAAW,CAAC;IAEnC;;;;;OAKG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED;;;;;;OAMG;IACI,QAAQ,IAAI,gBAAgB;yCAzBjB,aAAa;2CAAb,aAAa;CAgClC"}
1
+ {"version":3,"file":"base-form-panel.d.ts","sourceRoot":"","sources":["../../../src/lib/panel-slot/base-form-panel.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,MAAM,aAAa,GACnB,UAAU,GACV,eAAe,GACf,cAAc,GACd,eAAe,GACf,kBAAkB,CAAC;AAEzB;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,6BAA8B,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,8BACsB,aAAa,CAAC,OAAO,SAAS,UAAU,GAAG,UAAU;IACvE,6EAA6E;IACpE,MAAM,EAAG,OAAO,CAAC;IAC1B,kGAAkG;IACzF,aAAa,EAAG,iBAAiB,CAAC;IAC3C,qGAAqG;IAC5F,WAAW,CAAC,EAAE,WAAW,CAAC;IAEnC;;;;;OAKG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED;;;;;;OAMG;IACI,QAAQ,IAAI,gBAAgB;yCAzBjB,aAAa;2CAAb,aAAa;CAgClC"}
@@ -1 +1 @@
1
- {"version":3,"file":"base-form-panel.js","sourceRoot":"","sources":["../../../src/lib/panel-slot/base-form-panel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAc,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;;AA4DpE;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,OAAgB,aAAa;IAC/B,6EAA6E;IACpE,MAAM,CAAW;IAC1B,kGAAkG;IACzF,aAAa,CAAqB;IAC3C,qGAAqG;IAC5F,WAAW,CAAe;IAEnC;;;;;OAKG;IACH,IAAW,QAAQ;QACf,OAAO,IAAI,CAAC,aAAa,EAAE,QAAQ,IAAI,KAAK,CAAC;IACjD,CAAC;IAED;;;;;;OAMG;IACI,QAAQ;QACX,6EAA6E;QAC7E,6DAA6D;QAC7D,MAAM,MAAM,GAAG,IAAI,gBAAgB,EAAE,CAAC;QACtC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,OAAO,MAAM,CAAC;IAClB,CAAC;uGA/BiB,aAAa;6DAAb,aAAa;;iFAAb,aAAa;cADlC,SAAS;;kBAGL,KAAK;;kBAEL,KAAK;;kBAEL,KAAK","sourcesContent":["import { Directive, Input } from '@angular/core';\nimport { BaseEntity, ValidationResult } from '@memberjunction/core';\nimport { BaseFormComponent } from '../base-form-component';\nimport { FormContext } from '../types/form-types';\n\n/**\n * Well-known slot positions where panels can be injected into a generated\n * entity form. Used as the `slot` field in a panel's registration metadata.\n *\n * Slot positions inside the form, top to bottom:\n *\n * ┌─ <mj-record-form-container>\n * │ [top-area slot] ← CodeGen-emitted; rare (entities with top-area section)\n * │ [before-fields slot] ← CodeGen-emitted; banners / warnings above field panels\n * │ <connection details>\n * │ <other generated field panels>\n * │ [after-fields slot] ← CodeGen-emitted; typed config knobs, secondary settings — most common slot\n * │ <content items grid>\n * │ <other related-entity grids>\n * │ [after-related slot] ← CodeGen-emitted; bottom-of-form addenda\n * │ [after-everything slot] ← Container-emitted; ALWAYS present (fallback target)\n * └─ </mj-record-form-container>\n *\n * Pick the slot that matches the panel's role. `after-fields` is the default\n * for most \"additional settings\" panels — it sits in the field column, before\n * the related-entity grids.\n *\n * **Fallback behavior** — see `FormSlotCoordinator`. If the registered slot is\n * missing in the current form (CodeGen hasn't been rerun against the new\n * template emitter), the panel walks forward in the chain until it finds an\n * existing slot. `after-everything` is always emitted by the container, so\n * fallback never dead-ends. Pre-CodeGen-regen forms show every panel at the\n * bottom of the form; post-regen forms show them in the preferred position.\n */\nexport type FormPanelSlot =\n | 'top-area'\n | 'before-fields'\n | 'after-fields'\n | 'after-related'\n | 'after-everything';\n\n/**\n * Shape of the `metadata` object every BaseFormPanel registration MUST attach\n * via `@RegisterClass(BaseFormPanel, { metadata: {...} })`. The\n * `<mj-form-panel-slot>` host filters registrations by matching against this\n * shape — registrations without it are silently ignored.\n *\n * - `entity` — the `MJContentSourceEntity_IContentSourceConfiguration`-style\n * entity name. Must match the entity-form's record entity exactly\n * (case-sensitive trim-equality enforced by the slot host).\n * - `slot` — which slot the panel renders into.\n * - `sortKey`— higher = earlier within the slot. Defaults to 0 if omitted.\n * Use ranges (e.g., 100/50/10) so future panels can wedge in\n * without recomputing every neighbor's sort.\n */\nexport interface FormPanelRegistrationMetadata extends Record<string, unknown> {\n entity: string;\n slot: FormPanelSlot;\n sortKey?: number;\n}\n\n/**\n * Abstract base for every dynamically-injectable form panel.\n *\n * Subclasses are standalone Angular components decorated with\n * `@RegisterClass(BaseFormPanel, { metadata: { entity, slot, sortKey? } })`.\n * At runtime, `<mj-form-panel-slot>` discovers matching registrations via\n * `ClassFactory.GetAllRegistrationsByMetadata`, instantiates each via\n * `ViewContainerRef.createComponent`, and wires `Record` + `FormComponent` +\n * `FormContext` into the inputs.\n *\n * Lifecycle hooks: standard Angular `ngOnInit` / `ngOnDestroy` work as usual.\n * `Record` is guaranteed to be set before the first change-detection pass.\n *\n * Optional `validate()` returns a synchronous validation result; the parent\n * `BaseFormComponent.Save()` path will surface it via the existing validation\n * pipeline when called. Panels that don't validate anything beyond what the\n * record itself does can leave this method off.\n */\n@Directive()\nexport abstract class BaseFormPanel<TRecord extends BaseEntity = BaseEntity> {\n /** The entity record being edited. Set by the slot host before view init. */\n @Input() Record!: TRecord;\n /** The host form component (use for EditMode, dirty notifications, etc). Set by the slot host. */\n @Input() FormComponent!: BaseFormComponent;\n /** Optional form context — same shape the collapsible-panel chrome expects. Set by the slot host. */\n @Input() FormContext?: FormContext;\n\n /**\n * Convenience getter for read-only / edit-mode rendering. Falls back to\n * `false` when the panel is shown outside a `BaseFormComponent` host (e.g.,\n * a dashboard quick-edit dialog reusing the same panel — see the\n * \"composable panel\" docs).\n */\n public get EditMode(): boolean {\n return this.FormComponent?.EditMode ?? false;\n }\n\n /**\n * Override to add per-panel validation that should block the parent form's\n * Save(). Return `{ Success: true, Errors: [] }` when valid, or include\n * `ValidationErrorInfo` entries to surface field-level errors. The default\n * implementation reports valid (panels that don't need extra validation\n * can leave this method off).\n */\n public validate(): ValidationResult {\n // Inline construction — ValidationResult is a class in @memberjunction/core,\n // not a plain interface, so callers can construct via `new`.\n const result = new ValidationResult();\n result.Success = true;\n return result;\n }\n}\n"]}
1
+ {"version":3,"file":"base-form-panel.js","sourceRoot":"","sources":["../../../src/lib/panel-slot/base-form-panel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAc,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;;AA+DpE;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,OAAgB,aAAa;IAC/B,6EAA6E;IACpE,MAAM,CAAW;IAC1B,kGAAkG;IACzF,aAAa,CAAqB;IAC3C,qGAAqG;IAC5F,WAAW,CAAe;IAEnC;;;;;OAKG;IACH,IAAW,QAAQ;QACf,OAAO,IAAI,CAAC,aAAa,EAAE,QAAQ,IAAI,KAAK,CAAC;IACjD,CAAC;IAED;;;;;;OAMG;IACI,QAAQ;QACX,6EAA6E;QAC7E,6DAA6D;QAC7D,MAAM,MAAM,GAAG,IAAI,gBAAgB,EAAE,CAAC;QACtC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,OAAO,MAAM,CAAC;IAClB,CAAC;uGA/BiB,aAAa;6DAAb,aAAa;;iFAAb,aAAa;cADlC,SAAS;;kBAGL,KAAK;;kBAEL,KAAK;;kBAEL,KAAK","sourcesContent":["import { Directive, Input } from '@angular/core';\nimport { BaseEntity, ValidationResult } from '@memberjunction/core';\nimport { BaseFormComponent } from '../base-form-component';\nimport { FormContext } from '../types/form-types';\n\n/**\n * Well-known slot positions where panels can be injected into a generated\n * entity form. Used as the `slot` field in a panel's registration metadata.\n *\n * Slot positions inside the form, top to bottom:\n *\n * ┌─ <mj-record-form-container>\n * │ [top-area slot] ← CodeGen-emitted; rare (entities with top-area section)\n * │ [before-fields slot] ← CodeGen-emitted; banners / warnings above field panels\n * │ <connection details>\n * │ <other generated field panels>\n * │ [after-fields slot] ← CodeGen-emitted; typed config knobs, secondary settings — most common slot\n * │ <content items grid>\n * │ <other related-entity grids>\n * │ [after-related slot] ← CodeGen-emitted; bottom-of-form addenda\n * │ [after-everything slot] ← Container-emitted; ALWAYS present (fallback target)\n * └─ </mj-record-form-container>\n *\n * Pick the slot that matches the panel's role. `after-fields` is the default\n * for most \"additional settings\" panels — it sits in the field column, before\n * the related-entity grids.\n *\n * **Fallback behavior** — see `FormSlotCoordinator`. If the registered slot is\n * missing in the current form (CodeGen hasn't been rerun against the new\n * template emitter), the panel walks forward in the chain until it finds an\n * existing slot. `after-everything` is always emitted by the container, so\n * fallback never dead-ends. Pre-CodeGen-regen forms show every panel at the\n * bottom of the form; post-regen forms show them in the preferred position.\n */\nexport type FormPanelSlot =\n | 'top-area'\n | 'before-fields'\n | 'after-fields'\n | 'after-related'\n | 'after-everything';\n\n/**\n * Shape of the `metadata` object every BaseFormPanel registration MUST attach\n * via `@RegisterClass(BaseFormPanel, { metadata: {...} })`. The\n * `<mj-form-panel-slot>` host filters registrations by matching against this\n * shape — registrations without it are silently ignored.\n *\n * - `entity` — the exact entity name (case-sensitive equality enforced by the\n * slot host), OR the wildcard `'*'` to register an\n * **entity-agnostic** panel that mounts on EVERY entity's form.\n * Wildcard panels are expected to self-hide (render nothing) when\n * they don't apply to the current record, so the cross-cutting\n * registration stays unobtrusive on forms it has no business on.\n * - `slot` — which slot the panel renders into.\n * - `sortKey`— higher = earlier within the slot. Defaults to 0 if omitted.\n * Use ranges (e.g., 100/50/10) so future panels can wedge in\n * without recomputing every neighbor's sort.\n */\nexport interface FormPanelRegistrationMetadata extends Record<string, unknown> {\n entity: string;\n slot: FormPanelSlot;\n sortKey?: number;\n}\n\n/**\n * Abstract base for every dynamically-injectable form panel.\n *\n * Subclasses are standalone Angular components decorated with\n * `@RegisterClass(BaseFormPanel, { metadata: { entity, slot, sortKey? } })`.\n * At runtime, `<mj-form-panel-slot>` discovers matching registrations via\n * `ClassFactory.GetAllRegistrationsByMetadata`, instantiates each via\n * `ViewContainerRef.createComponent`, and wires `Record` + `FormComponent` +\n * `FormContext` into the inputs.\n *\n * Lifecycle hooks: standard Angular `ngOnInit` / `ngOnDestroy` work as usual.\n * `Record` is guaranteed to be set before the first change-detection pass.\n *\n * Optional `validate()` returns a synchronous validation result; the parent\n * `BaseFormComponent.Save()` path will surface it via the existing validation\n * pipeline when called. Panels that don't validate anything beyond what the\n * record itself does can leave this method off.\n */\n@Directive()\nexport abstract class BaseFormPanel<TRecord extends BaseEntity = BaseEntity> {\n /** The entity record being edited. Set by the slot host before view init. */\n @Input() Record!: TRecord;\n /** The host form component (use for EditMode, dirty notifications, etc). Set by the slot host. */\n @Input() FormComponent!: BaseFormComponent;\n /** Optional form context — same shape the collapsible-panel chrome expects. Set by the slot host. */\n @Input() FormContext?: FormContext;\n\n /**\n * Convenience getter for read-only / edit-mode rendering. Falls back to\n * `false` when the panel is shown outside a `BaseFormComponent` host (e.g.,\n * a dashboard quick-edit dialog reusing the same panel — see the\n * \"composable panel\" docs).\n */\n public get EditMode(): boolean {\n return this.FormComponent?.EditMode ?? false;\n }\n\n /**\n * Override to add per-panel validation that should block the parent form's\n * Save(). Return `{ Success: true, Errors: [] }` when valid, or include\n * `ValidationErrorInfo` entries to surface field-level errors. The default\n * implementation reports valid (panels that don't need extra validation\n * can leave this method off).\n */\n public validate(): ValidationResult {\n // Inline construction — ValidationResult is a class in @memberjunction/core,\n // not a plain interface, so callers can construct via `new`.\n const result = new ValidationResult();\n result.Success = true;\n return result;\n }\n}\n"]}
@@ -60,8 +60,16 @@ export declare class FormPanelSlotComponent implements OnInit, OnChanges, OnDest
60
60
  ngOnChanges(changes: SimpleChanges): void;
61
61
  ngOnDestroy(): void;
62
62
  private remount;
63
- /** Find registrations whose metadata declares this exact entity + slot. */
63
+ /**
64
+ * Find registrations whose metadata declares this exact entity (or the
65
+ * `'*'` wildcard) + slot. A panel registered with `entity: '*'` is
66
+ * entity-agnostic — it mounts on EVERY entity's form. Such panels are
67
+ * expected to self-hide (render nothing) when they don't apply to the
68
+ * current record, so the cross-cutting registration stays unobtrusive.
69
+ */
64
70
  private findRegistrations;
71
+ /** True when a registration's `entity` matches this slot's entity, or is the `'*'` wildcard. */
72
+ private entityMatches;
65
73
  /**
66
74
  * Find panels registered for a different slot that should mount HERE
67
75
  * because their preferred slot isn't present and this slot is the next
@@ -1 +1 @@
1
- {"version":3,"file":"form-panel-slot.component.d.ts","sourceRoot":"","sources":["../../../src/lib/panel-slot/form-panel-slot.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,SAAS,EACT,SAAS,EACT,MAAM,EAEN,aAAa,EAIhB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,UAAU,EAAY,MAAM,sBAAsB,CAAC;AAE5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAgD,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAChG,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;;AAEtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAWa,sBAAuB,YAAW,MAAM,EAAE,SAAS,EAAE,SAAS;IAsB/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IArBrD,oEAAoE;IAC3D,MAAM,EAAG,MAAM,CAAC;IACzB,gDAAgD;IACvC,IAAI,EAAG,aAAa,CAAC;IAC9B,yEAAyE;IAChE,MAAM,EAAG,UAAU,CAAC;IAC7B,mFAAmF;IAC1E,aAAa,EAAG,iBAAiB,CAAC;IAC3C,2EAA2E;IAClE,WAAW,CAAC,EAAE,WAAW,CAAC;IAGnC,OAAO,CAAC,MAAM,CAAoB;IAElC,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuB;IAChD,OAAO,CAAC,cAAc,CAA8B;IACpD;gFAC4E;IAC5E,OAAO,CAAC,YAAY,CAAK;gBAEgB,WAAW,CAAC,EAAE,mBAAmB,YAAA;IAE1E,QAAQ,IAAI,IAAI;IAahB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IA4BzC,WAAW,IAAI,IAAI;IAUnB,OAAO,CAAC,OAAO;IAqDf,2EAA2E;IAC3E,OAAO,CAAC,iBAAiB;IAWzB;;;;;;;OAOG;IACH,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,UAAU;yCAxKT,sBAAsB;2CAAtB,sBAAsB;CA4KlC"}
1
+ {"version":3,"file":"form-panel-slot.component.d.ts","sourceRoot":"","sources":["../../../src/lib/panel-slot/form-panel-slot.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,SAAS,EACT,SAAS,EACT,MAAM,EAEN,aAAa,EAIhB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,UAAU,EAAY,MAAM,sBAAsB,CAAC;AAE5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAgD,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAChG,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;;AAEtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAWa,sBAAuB,YAAW,MAAM,EAAE,SAAS,EAAE,SAAS;IAsB/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IArBrD,oEAAoE;IAC3D,MAAM,EAAG,MAAM,CAAC;IACzB,gDAAgD;IACvC,IAAI,EAAG,aAAa,CAAC;IAC9B,yEAAyE;IAChE,MAAM,EAAG,UAAU,CAAC;IAC7B,mFAAmF;IAC1E,aAAa,EAAG,iBAAiB,CAAC;IAC3C,2EAA2E;IAClE,WAAW,CAAC,EAAE,WAAW,CAAC;IAGnC,OAAO,CAAC,MAAM,CAAoB;IAElC,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuB;IAChD,OAAO,CAAC,cAAc,CAA8B;IACpD;gFAC4E;IAC5E,OAAO,CAAC,YAAY,CAAK;gBAEgB,WAAW,CAAC,EAAE,mBAAmB,YAAA;IAE1E,QAAQ,IAAI,IAAI;IAahB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IA4BzC,WAAW,IAAI,IAAI;IAUnB,OAAO,CAAC,OAAO;IAqDf;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAWzB,gGAAgG;IAChG,OAAO,CAAC,aAAa;IAIrB;;;;;;;OAOG;IACH,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,UAAU;yCAnLT,sBAAsB;2CAAtB,sBAAsB;CAuLlC"}
@@ -156,15 +156,25 @@ export class FormPanelSlotComponent {
156
156
  }
157
157
  this.remountDepth--;
158
158
  }
159
- /** Find registrations whose metadata declares this exact entity + slot. */
159
+ /**
160
+ * Find registrations whose metadata declares this exact entity (or the
161
+ * `'*'` wildcard) + slot. A panel registered with `entity: '*'` is
162
+ * entity-agnostic — it mounts on EVERY entity's form. Such panels are
163
+ * expected to self-hide (render nothing) when they don't apply to the
164
+ * current record, so the cross-cutting registration stays unobtrusive.
165
+ */
160
166
  findRegistrations(slot) {
161
167
  return MJGlobal.Instance.ClassFactory.GetAllRegistrationsByMetadata(BaseFormPanel, (metadata) => {
162
168
  if (!metadata)
163
169
  return false;
164
170
  const meta = metadata;
165
- return meta.entity === this.Entity && meta.slot === slot;
171
+ return this.entityMatches(meta.entity) && meta.slot === slot;
166
172
  });
167
173
  }
174
+ /** True when a registration's `entity` matches this slot's entity, or is the `'*'` wildcard. */
175
+ entityMatches(registeredEntity) {
176
+ return registeredEntity === '*' || registeredEntity === this.Entity;
177
+ }
168
178
  /**
169
179
  * Find panels registered for a different slot that should mount HERE
170
180
  * because their preferred slot isn't present and this slot is the next
@@ -180,7 +190,7 @@ export class FormPanelSlotComponent {
180
190
  if (!metadata)
181
191
  return false;
182
192
  const meta = metadata;
183
- return meta.entity === this.Entity && meta.slot != null && meta.slot !== this.Slot;
193
+ return this.entityMatches(meta.entity) && meta.slot != null && meta.slot !== this.Slot;
184
194
  });
185
195
  return allForEntity.filter(reg => {
186
196
  const meta = reg.Metadata;
@@ -1 +1 @@
1
- {"version":3,"file":"form-panel-slot.component.js","sourceRoot":"","sources":["../../../src/lib/panel-slot/form-panel-slot.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n ComponentRef,\n Input,\n OnChanges,\n OnDestroy,\n OnInit,\n Optional,\n SimpleChanges,\n Type,\n ViewChild,\n ViewContainerRef,\n} from '@angular/core';\nimport { Subject, takeUntil } from 'rxjs';\nimport { BaseEntity, LogError } from '@memberjunction/core';\nimport { ClassRegistration, MJGlobal } from '@memberjunction/global';\nimport { BaseFormComponent } from '../base-form-component';\nimport { FormContext } from '../types/form-types';\nimport { BaseFormPanel, FormPanelRegistrationMetadata, FormPanelSlot } from './base-form-panel';\nimport { FormSlotCoordinator } from './form-slot-coordinator.service';\n\n/**\n * `<mj-form-panel-slot>` — dynamic slot host that discovers and mounts every\n * registered `BaseFormPanel` whose metadata matches the slot's `Entity` +\n * `Slot` inputs. Generated entity-form HTML emits one of these at each\n * well-known slot position so consumers can extend the form WITHOUT replacing\n * it via the custom-form override pattern.\n *\n * **Discovery**: uses `ClassFactory.GetAllRegistrationsByMetadata(BaseFormPanel, ...)`\n * to find every panel registered with metadata `{ entity, slot }` matching this\n * slot. Registrations without a metadata bag (or without these required keys)\n * are silently ignored so legacy registrations don't accidentally bind.\n *\n * **Ordering**: registrations are sorted by `metadata.sortKey` descending —\n * higher number renders first within the slot. Use ranges (100/50/10) so future\n * panels can wedge in without renumbering. Ties fall back to the\n * `ClassRegistration.Priority` field (also descending), and finally to\n * registration order.\n *\n * **Fallback chain** (see `FormSlotCoordinator`): each slot host registers\n * with the per-form coordinator and additionally renders panels whose\n * REGISTERED slot is missing in the current form, when this host happens to\n * be the next-existing slot down the chain. The container template guarantees\n * an `after-everything` slot host, so fallback always terminates somewhere.\n * Downstream consumers running an older form template (no CodeGen rerun\n * yet) get every panel rendered at the bottom; consumers on the new template\n * get them in the preferred position.\n *\n * **Re-mount**: any input change OR a coordinator change (another slot\n * registers/deregisters) triggers a re-render so fallback assignments stay\n * correct as slots come and go.\n */\n@Component({\n standalone: false,\n selector: 'mj-form-panel-slot',\n template: `<ng-container #anchor></ng-container>`,\n // `display: contents` makes the host element disappear from the layout\n // tree so the mounted panels (children) participate directly in the\n // parent's flex/grid layout. Without this, the parent's gap/spacing\n // rules treat all mounted panels as a SINGLE child (the slot host),\n // collapsing the spacing between them.\n styles: [`:host { display: contents; }`],\n})\nexport class FormPanelSlotComponent implements OnInit, OnChanges, OnDestroy {\n /** The entity name (e.g., `\"MJ: Content Sources\"`) being edited. */\n @Input() Entity!: string;\n /** Which slot position this host represents. */\n @Input() Slot!: FormPanelSlot;\n /** The record being edited — threaded through to every mounted panel. */\n @Input() Record!: BaseEntity;\n /** The host form component — used by panels for EditMode + dirty notifications. */\n @Input() FormComponent!: BaseFormComponent;\n /** Optional form context — same shape collapsible-panel chrome expects. */\n @Input() FormContext?: FormContext;\n\n @ViewChild('anchor', { read: ViewContainerRef, static: true })\n private anchor!: ViewContainerRef;\n\n private mounted: ComponentRef<BaseFormPanel>[] = [];\n private readonly destroy$ = new Subject<void>();\n private registeredSlot: FormPanelSlot | null = null;\n /** Tracks synchronous re-entry depth into remount() so a future refactor\n * that reintroduces a remount loop surfaces loudly instead of freezing. */\n private remountDepth = 0;\n\n constructor(@Optional() private readonly coordinator?: FormSlotCoordinator) {}\n\n ngOnInit(): void {\n if (this.Slot && this.coordinator) {\n this.coordinator.registerSlot(this.Slot);\n this.registeredSlot = this.Slot;\n // Re-mount whenever the coordinator's known-slot set changes —\n // a panel that was previously orphaned to us might now have a\n // better slot available (or vice versa).\n this.coordinator.changes\n .pipe(takeUntil(this.destroy$))\n .subscribe(() => this.remount());\n }\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (!this.Entity || !this.Slot || !this.Record || !this.FormComponent) {\n // Inputs still being wired — wait for the next pass.\n return;\n }\n\n // Only remount when something that affects WHICH panels to mount has\n // changed. FormContext only flows through to mounted panels — pushing\n // it to the existing panels avoids an unmount/remount cycle.\n //\n // Critical for performance: the host form often reconstructs\n // formContext on every CD pass. Treating that as a structural change\n // would loop (remount → detectChanges → another CD → new FormContext\n // → remount again).\n const meaningfulKeys = ['Entity', 'Slot', 'Record', 'FormComponent'];\n const meaningfulChange = meaningfulKeys.some(k => changes[k]?.currentValue !== changes[k]?.previousValue);\n if (!meaningfulChange && changes['FormContext'] && this.mounted.length > 0) {\n for (const ref of this.mounted) {\n ref.instance.FormContext = this.FormContext;\n }\n return;\n }\n if (!meaningfulChange && this.mounted.length > 0) {\n return;\n }\n this.remount();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n if (this.coordinator && this.registeredSlot) {\n this.coordinator.deregisterSlot(this.registeredSlot);\n this.registeredSlot = null;\n }\n this.unmountAll();\n }\n\n private remount(): void {\n this.remountDepth++;\n if (this.remountDepth > 5) {\n console.error(`[mj-form-panel-slot] LOOP DETECTED — remount depth ${this.remountDepth} on slot=${this.Slot}. Bailing.`);\n this.remountDepth--;\n return;\n }\n this.unmountAll();\n\n // 1. Direct matches: panels registered for THIS slot.\n const direct = this.findRegistrations(this.Slot);\n\n // 2. Orphans: panels registered for some OTHER slot whose preferred\n // slot doesn't exist in this form, AND this slot host is the\n // coordinator-resolved fallback for them. Without a coordinator\n // (no parent container), no orphan handling — panels just bind\n // to their literal slot.\n const orphans = this.findOrphans();\n\n const all = [...direct, ...orphans];\n if (all.length === 0) {\n this.remountDepth--;\n return;\n }\n\n // Sort: higher sortKey first, then higher Priority, then registration order.\n all.sort((a, b) => {\n const aSort = (a.Metadata as FormPanelRegistrationMetadata | undefined)?.sortKey ?? 0;\n const bSort = (b.Metadata as FormPanelRegistrationMetadata | undefined)?.sortKey ?? 0;\n if (aSort !== bSort) return bSort - aSort;\n return b.Priority - a.Priority;\n });\n\n for (const reg of all) {\n try {\n const ctor = reg.SubClass as Type<BaseFormPanel>;\n const ref = this.anchor.createComponent(ctor);\n ref.instance.Record = this.Record;\n ref.instance.FormComponent = this.FormComponent;\n if (this.FormContext) ref.instance.FormContext = this.FormContext;\n // No detectChanges() — Angular's normal CD pass picks the new\n // component up. Calling detectChanges() synchronously inside\n // an ongoing CD cycle (which is when ngOnChanges → remount\n // fires) can re-enter and cause an infinite loop if anything\n // upstream is reconstructing inputs on each CD pass.\n this.mounted.push(ref);\n } catch (e) {\n LogError(`[mj-form-panel-slot] Failed to mount panel for ${this.Entity}:${this.Slot}: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n this.remountDepth--;\n }\n\n /** Find registrations whose metadata declares this exact entity + slot. */\n private findRegistrations(slot: FormPanelSlot): ClassRegistration[] {\n return MJGlobal.Instance.ClassFactory.GetAllRegistrationsByMetadata(\n BaseFormPanel,\n (metadata) => {\n if (!metadata) return false;\n const meta = metadata as Partial<FormPanelRegistrationMetadata>;\n return meta.entity === this.Entity && meta.slot === slot;\n },\n );\n }\n\n /**\n * Find panels registered for a different slot that should mount HERE\n * because their preferred slot isn't present and this slot is the next\n * existing one in the fallback chain.\n *\n * Only meaningful when we have a coordinator (i.e., we're inside an\n * `<mj-record-form-container>`).\n */\n private findOrphans(): ClassRegistration[] {\n if (!this.coordinator) return [];\n\n const allForEntity = MJGlobal.Instance.ClassFactory.GetAllRegistrationsByMetadata(\n BaseFormPanel,\n (metadata) => {\n if (!metadata) return false;\n const meta = metadata as Partial<FormPanelRegistrationMetadata>;\n return meta.entity === this.Entity && meta.slot != null && meta.slot !== this.Slot;\n },\n );\n\n return allForEntity.filter(reg => {\n const meta = reg.Metadata as FormPanelRegistrationMetadata | undefined;\n if (!meta?.slot) return false;\n const resolved = this.coordinator!.resolveSlot(meta.slot);\n return resolved === this.Slot;\n });\n }\n\n private unmountAll(): void {\n this.anchor.clear();\n this.mounted = [];\n }\n}\n"]}
1
+ {"version":3,"file":"form-panel-slot.component.js","sourceRoot":"","sources":["../../../src/lib/panel-slot/form-panel-slot.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n ComponentRef,\n Input,\n OnChanges,\n OnDestroy,\n OnInit,\n Optional,\n SimpleChanges,\n Type,\n ViewChild,\n ViewContainerRef,\n} from '@angular/core';\nimport { Subject, takeUntil } from 'rxjs';\nimport { BaseEntity, LogError } from '@memberjunction/core';\nimport { ClassRegistration, MJGlobal } from '@memberjunction/global';\nimport { BaseFormComponent } from '../base-form-component';\nimport { FormContext } from '../types/form-types';\nimport { BaseFormPanel, FormPanelRegistrationMetadata, FormPanelSlot } from './base-form-panel';\nimport { FormSlotCoordinator } from './form-slot-coordinator.service';\n\n/**\n * `<mj-form-panel-slot>` — dynamic slot host that discovers and mounts every\n * registered `BaseFormPanel` whose metadata matches the slot's `Entity` +\n * `Slot` inputs. Generated entity-form HTML emits one of these at each\n * well-known slot position so consumers can extend the form WITHOUT replacing\n * it via the custom-form override pattern.\n *\n * **Discovery**: uses `ClassFactory.GetAllRegistrationsByMetadata(BaseFormPanel, ...)`\n * to find every panel registered with metadata `{ entity, slot }` matching this\n * slot. Registrations without a metadata bag (or without these required keys)\n * are silently ignored so legacy registrations don't accidentally bind.\n *\n * **Ordering**: registrations are sorted by `metadata.sortKey` descending —\n * higher number renders first within the slot. Use ranges (100/50/10) so future\n * panels can wedge in without renumbering. Ties fall back to the\n * `ClassRegistration.Priority` field (also descending), and finally to\n * registration order.\n *\n * **Fallback chain** (see `FormSlotCoordinator`): each slot host registers\n * with the per-form coordinator and additionally renders panels whose\n * REGISTERED slot is missing in the current form, when this host happens to\n * be the next-existing slot down the chain. The container template guarantees\n * an `after-everything` slot host, so fallback always terminates somewhere.\n * Downstream consumers running an older form template (no CodeGen rerun\n * yet) get every panel rendered at the bottom; consumers on the new template\n * get them in the preferred position.\n *\n * **Re-mount**: any input change OR a coordinator change (another slot\n * registers/deregisters) triggers a re-render so fallback assignments stay\n * correct as slots come and go.\n */\n@Component({\n standalone: false,\n selector: 'mj-form-panel-slot',\n template: `<ng-container #anchor></ng-container>`,\n // `display: contents` makes the host element disappear from the layout\n // tree so the mounted panels (children) participate directly in the\n // parent's flex/grid layout. Without this, the parent's gap/spacing\n // rules treat all mounted panels as a SINGLE child (the slot host),\n // collapsing the spacing between them.\n styles: [`:host { display: contents; }`],\n})\nexport class FormPanelSlotComponent implements OnInit, OnChanges, OnDestroy {\n /** The entity name (e.g., `\"MJ: Content Sources\"`) being edited. */\n @Input() Entity!: string;\n /** Which slot position this host represents. */\n @Input() Slot!: FormPanelSlot;\n /** The record being edited — threaded through to every mounted panel. */\n @Input() Record!: BaseEntity;\n /** The host form component — used by panels for EditMode + dirty notifications. */\n @Input() FormComponent!: BaseFormComponent;\n /** Optional form context — same shape collapsible-panel chrome expects. */\n @Input() FormContext?: FormContext;\n\n @ViewChild('anchor', { read: ViewContainerRef, static: true })\n private anchor!: ViewContainerRef;\n\n private mounted: ComponentRef<BaseFormPanel>[] = [];\n private readonly destroy$ = new Subject<void>();\n private registeredSlot: FormPanelSlot | null = null;\n /** Tracks synchronous re-entry depth into remount() so a future refactor\n * that reintroduces a remount loop surfaces loudly instead of freezing. */\n private remountDepth = 0;\n\n constructor(@Optional() private readonly coordinator?: FormSlotCoordinator) {}\n\n ngOnInit(): void {\n if (this.Slot && this.coordinator) {\n this.coordinator.registerSlot(this.Slot);\n this.registeredSlot = this.Slot;\n // Re-mount whenever the coordinator's known-slot set changes —\n // a panel that was previously orphaned to us might now have a\n // better slot available (or vice versa).\n this.coordinator.changes\n .pipe(takeUntil(this.destroy$))\n .subscribe(() => this.remount());\n }\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (!this.Entity || !this.Slot || !this.Record || !this.FormComponent) {\n // Inputs still being wired — wait for the next pass.\n return;\n }\n\n // Only remount when something that affects WHICH panels to mount has\n // changed. FormContext only flows through to mounted panels — pushing\n // it to the existing panels avoids an unmount/remount cycle.\n //\n // Critical for performance: the host form often reconstructs\n // formContext on every CD pass. Treating that as a structural change\n // would loop (remount → detectChanges → another CD → new FormContext\n // → remount again).\n const meaningfulKeys = ['Entity', 'Slot', 'Record', 'FormComponent'];\n const meaningfulChange = meaningfulKeys.some(k => changes[k]?.currentValue !== changes[k]?.previousValue);\n if (!meaningfulChange && changes['FormContext'] && this.mounted.length > 0) {\n for (const ref of this.mounted) {\n ref.instance.FormContext = this.FormContext;\n }\n return;\n }\n if (!meaningfulChange && this.mounted.length > 0) {\n return;\n }\n this.remount();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n if (this.coordinator && this.registeredSlot) {\n this.coordinator.deregisterSlot(this.registeredSlot);\n this.registeredSlot = null;\n }\n this.unmountAll();\n }\n\n private remount(): void {\n this.remountDepth++;\n if (this.remountDepth > 5) {\n console.error(`[mj-form-panel-slot] LOOP DETECTED — remount depth ${this.remountDepth} on slot=${this.Slot}. Bailing.`);\n this.remountDepth--;\n return;\n }\n this.unmountAll();\n\n // 1. Direct matches: panels registered for THIS slot.\n const direct = this.findRegistrations(this.Slot);\n\n // 2. Orphans: panels registered for some OTHER slot whose preferred\n // slot doesn't exist in this form, AND this slot host is the\n // coordinator-resolved fallback for them. Without a coordinator\n // (no parent container), no orphan handling — panels just bind\n // to their literal slot.\n const orphans = this.findOrphans();\n\n const all = [...direct, ...orphans];\n if (all.length === 0) {\n this.remountDepth--;\n return;\n }\n\n // Sort: higher sortKey first, then higher Priority, then registration order.\n all.sort((a, b) => {\n const aSort = (a.Metadata as FormPanelRegistrationMetadata | undefined)?.sortKey ?? 0;\n const bSort = (b.Metadata as FormPanelRegistrationMetadata | undefined)?.sortKey ?? 0;\n if (aSort !== bSort) return bSort - aSort;\n return b.Priority - a.Priority;\n });\n\n for (const reg of all) {\n try {\n const ctor = reg.SubClass as Type<BaseFormPanel>;\n const ref = this.anchor.createComponent(ctor);\n ref.instance.Record = this.Record;\n ref.instance.FormComponent = this.FormComponent;\n if (this.FormContext) ref.instance.FormContext = this.FormContext;\n // No detectChanges() — Angular's normal CD pass picks the new\n // component up. Calling detectChanges() synchronously inside\n // an ongoing CD cycle (which is when ngOnChanges → remount\n // fires) can re-enter and cause an infinite loop if anything\n // upstream is reconstructing inputs on each CD pass.\n this.mounted.push(ref);\n } catch (e) {\n LogError(`[mj-form-panel-slot] Failed to mount panel for ${this.Entity}:${this.Slot}: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n this.remountDepth--;\n }\n\n /**\n * Find registrations whose metadata declares this exact entity (or the\n * `'*'` wildcard) + slot. A panel registered with `entity: '*'` is\n * entity-agnostic — it mounts on EVERY entity's form. Such panels are\n * expected to self-hide (render nothing) when they don't apply to the\n * current record, so the cross-cutting registration stays unobtrusive.\n */\n private findRegistrations(slot: FormPanelSlot): ClassRegistration[] {\n return MJGlobal.Instance.ClassFactory.GetAllRegistrationsByMetadata(\n BaseFormPanel,\n (metadata) => {\n if (!metadata) return false;\n const meta = metadata as Partial<FormPanelRegistrationMetadata>;\n return this.entityMatches(meta.entity) && meta.slot === slot;\n },\n );\n }\n\n /** True when a registration's `entity` matches this slot's entity, or is the `'*'` wildcard. */\n private entityMatches(registeredEntity: string | undefined): boolean {\n return registeredEntity === '*' || registeredEntity === this.Entity;\n }\n\n /**\n * Find panels registered for a different slot that should mount HERE\n * because their preferred slot isn't present and this slot is the next\n * existing one in the fallback chain.\n *\n * Only meaningful when we have a coordinator (i.e., we're inside an\n * `<mj-record-form-container>`).\n */\n private findOrphans(): ClassRegistration[] {\n if (!this.coordinator) return [];\n\n const allForEntity = MJGlobal.Instance.ClassFactory.GetAllRegistrationsByMetadata(\n BaseFormPanel,\n (metadata) => {\n if (!metadata) return false;\n const meta = metadata as Partial<FormPanelRegistrationMetadata>;\n return this.entityMatches(meta.entity) && meta.slot != null && meta.slot !== this.Slot;\n },\n );\n\n return allForEntity.filter(reg => {\n const meta = reg.Metadata as FormPanelRegistrationMetadata | undefined;\n if (!meta?.slot) return false;\n const resolved = this.coordinator!.resolveSlot(meta.slot);\n return resolved === this.Slot;\n });\n }\n\n private unmountAll(): void {\n this.anchor.clear();\n this.mounted = [];\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"form-resolver.service.d.ts","sourceRoot":"","sources":["../../../src/lib/resolver/form-resolver.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,IAAI,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,UAAU,EAAY,MAAM,sBAAsB,CAAC;AAGzF,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;;AAG3D;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,kEAAkE;AAClE,MAAM,MAAM,cAAc,GACpB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,qBAAqB,CAAC;IAAC,QAAQ,EAAE,qBAAqB,EAAE,CAAA;CAAE,GAC3F;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAAC,QAAQ,EAAE,qBAAqB,EAAE,CAAA;CAAE,GACvF;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,qBAAqB,EAAE,CAAA;CAAE,CAAC;AAiB1D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBACa,mBAAmB;IAEtB,oBAAoB,CACtB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,iBAAiB,GAC5B,OAAO,CAAC,cAAc,CAAC;IAc1B;;;;;OAKG;IACG,qBAAqB,CACvB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,iBAAiB,GAC5B,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAInC;;;;;;;;;;;OAWG;IACH,gBAAuB,yBAAyB,yBAAyB;IAEzE;;;OAGG;IACH,OAAO,CAAC,UAAU;IAIlB;;;;;;OAMG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAIhE;;;;;OAKG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAO5C;;;;;OAKG;IACH,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAK9C;;;;;OAKG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;YAMvC,sBAAsB;IAwEpC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,UAAU;yCA7LT,mBAAmB;6CAAnB,mBAAmB;CA6M/B"}
1
+ {"version":3,"file":"form-resolver.service.d.ts","sourceRoot":"","sources":["../../../src/lib/resolver/form-resolver.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,IAAI,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,UAAU,EAAY,MAAM,sBAAsB,CAAC;AAGzF,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;;AAG3D;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,kEAAkE;AAClE,MAAM,MAAM,cAAc,GACpB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,qBAAqB,CAAC;IAAC,QAAQ,EAAE,qBAAqB,EAAE,CAAA;CAAE,GAC3F;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAAC,QAAQ,EAAE,qBAAqB,EAAE,CAAA;CAAE,GACvF;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,qBAAqB,EAAE,CAAA;CAAE,CAAC;AAiB1D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBACa,mBAAmB;IAEtB,oBAAoB,CACtB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,iBAAiB,GAC5B,OAAO,CAAC,cAAc,CAAC;IAc1B;;;;;OAKG;IACG,qBAAqB,CACvB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,iBAAiB,GAC5B,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAInC;;;;;;;;;;;OAWG;IACH,gBAAuB,yBAAyB,yBAAyB;IAEzE;;;OAGG;IACH,OAAO,CAAC,UAAU;IAIlB;;;;;;OAMG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAIhE;;;;;OAKG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAO5C;;;;;OAKG;IACH,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAK9C;;;;;OAKG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;YAMvC,sBAAsB;IA8EpC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,UAAU;yCAnMT,mBAAmB;6CAAnB,mBAAmB;CAmN/B"}