@memberjunction/ng-base-forms 5.38.0 → 5.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -0
- package/dist/lib/base-form-component.d.ts +18 -0
- package/dist/lib/base-form-component.d.ts.map +1 -1
- package/dist/lib/base-form-component.js +31 -2
- package/dist/lib/base-form-component.js.map +1 -1
- package/dist/lib/container/record-form-container.component.d.ts +14 -0
- package/dist/lib/container/record-form-container.component.d.ts.map +1 -1
- package/dist/lib/container/record-form-container.component.js +59 -33
- package/dist/lib/container/record-form-container.component.js.map +1 -1
- package/dist/lib/field/fk-search-utils.d.ts +31 -0
- package/dist/lib/field/fk-search-utils.d.ts.map +1 -0
- package/dist/lib/field/fk-search-utils.js +47 -0
- package/dist/lib/field/fk-search-utils.js.map +1 -0
- package/dist/lib/field/form-field.component.d.ts +401 -6
- package/dist/lib/field/form-field.component.d.ts.map +1 -1
- package/dist/lib/field/form-field.component.js +1646 -214
- package/dist/lib/field/form-field.component.js.map +1 -1
- package/dist/lib/field/linked-field-options.d.ts +76 -0
- package/dist/lib/field/linked-field-options.d.ts.map +1 -0
- package/dist/lib/field/linked-field-options.js +80 -0
- package/dist/lib/field/linked-field-options.js.map +1 -0
- package/dist/lib/host/entity-form-host.component.d.ts +159 -0
- package/dist/lib/host/entity-form-host.component.d.ts.map +1 -0
- package/dist/lib/host/entity-form-host.component.js +539 -0
- package/dist/lib/host/entity-form-host.component.js.map +1 -0
- package/dist/lib/isa-related-panel/isa-related-card.component.js +2 -2
- package/dist/lib/overlays/base-form-overlay.d.ts +114 -0
- package/dist/lib/overlays/base-form-overlay.d.ts.map +1 -0
- package/dist/lib/overlays/base-form-overlay.js +227 -0
- package/dist/lib/overlays/base-form-overlay.js.map +1 -0
- package/dist/lib/overlays/form-dialog.component.d.ts +35 -0
- package/dist/lib/overlays/form-dialog.component.d.ts.map +1 -0
- package/dist/lib/overlays/form-dialog.component.js +92 -0
- package/dist/lib/overlays/form-dialog.component.js.map +1 -0
- package/dist/lib/overlays/form-presenter.service.d.ts +37 -0
- package/dist/lib/overlays/form-presenter.service.d.ts.map +1 -0
- package/dist/lib/overlays/form-presenter.service.js +130 -0
- package/dist/lib/overlays/form-presenter.service.js.map +1 -0
- package/dist/lib/overlays/form-presenter.types.d.ts +65 -0
- package/dist/lib/overlays/form-presenter.types.d.ts.map +1 -0
- package/dist/lib/overlays/form-presenter.types.js +25 -0
- package/dist/lib/overlays/form-presenter.types.js.map +1 -0
- package/dist/lib/overlays/form-slide-in.component.d.ts +38 -0
- package/dist/lib/overlays/form-slide-in.component.d.ts.map +1 -0
- package/dist/lib/overlays/form-slide-in.component.js +106 -0
- package/dist/lib/overlays/form-slide-in.component.js.map +1 -0
- package/dist/lib/overlays/form-window.component.d.ts +32 -0
- package/dist/lib/overlays/form-window.component.d.ts.map +1 -0
- package/dist/lib/overlays/form-window.component.js +93 -0
- package/dist/lib/overlays/form-window.component.js.map +1 -0
- package/dist/lib/panel/collapsible-panel.component.d.ts +19 -2
- package/dist/lib/panel/collapsible-panel.component.d.ts.map +1 -1
- package/dist/lib/panel/collapsible-panel.component.js +70 -13
- package/dist/lib/panel/collapsible-panel.component.js.map +1 -1
- package/dist/lib/resolver/form-resolver.service.d.ts +139 -0
- package/dist/lib/resolver/form-resolver.service.d.ts.map +1 -0
- package/dist/lib/resolver/form-resolver.service.js +235 -0
- package/dist/lib/resolver/form-resolver.service.js.map +1 -0
- package/dist/lib/section-manager/section-manager.component.js +2 -2
- package/dist/lib/toolbar/form-toolbar.component.js +2 -2
- package/dist/lib/types/entity-form-config.d.ts +125 -0
- package/dist/lib/types/entity-form-config.d.ts.map +1 -0
- package/dist/lib/types/entity-form-config.js +75 -0
- package/dist/lib/types/entity-form-config.js.map +1 -0
- package/dist/lib/types/form-types.d.ts +24 -0
- package/dist/lib/types/form-types.d.ts.map +1 -1
- package/dist/lib/types/form-types.js.map +1 -1
- package/dist/lib/types/navigation-events.d.ts +46 -2
- package/dist/lib/types/navigation-events.d.ts.map +1 -1
- package/dist/lib/types/navigation-events.js.map +1 -1
- package/dist/module.d.ts +12 -8
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +26 -7
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +11 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +14 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +17 -13
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-window.component.js","sourceRoot":"","sources":["../../../src/lib/overlays/form-window.component.ts","../../../src/lib/overlays/form-window.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAElH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;;;;;IC0BzD,AADF,yCAAmB,gBAC8D;IAAxB,wMAAS,oBAAa,KAAC;IAAC,YAAoB;IAAA,iBAAS;IAC5G,iCAA0D;IAArB,wMAAS,iBAAU,KAAC;IAAC,YAAsB;IAClF,AADkF,iBAAS,EACvE;;;IAFiB,cAAmB;IAAnB,wCAAmB;IAAyB,cAAoB;IAApB,2CAAoB;IAClF,cAAmB;IAAnB,wCAAmB;IAAsB,cAAsB;IAAtB,6CAAsB;;;;IAhCtF,oCAOuB;IAArB,4LAAS,iBAAU,KAAC;IAEpB,iDAiBuC;IAArC,AADA,AADA,AADA,AADA,AADA,AADA,AADA,AADA,4MAAS,0BAAmB,KAAC,iMAChB,wBAAiB,KAAC,2MAChB,4BAAqB,KAAC,qMACzB,4BAAqB,KAAC,6MAClB,gCAAyB,KAAC,+MACzB,iCAA0B,KAAC,qNACxB,oCAA6B,KAAC,qNAC9B,oCAA6B,KAAC,uMACrC,6BAAsB,KAAC;IACtC,iBAAsB;IAEtB,gHAAkB;IAMpB,iBAAY;;;IA7BV,AADA,AADA,AADA,AADA,AADA,8BAAgB,gCACQ,uBACT,yBACE,+BACM,+BACA;IAIrB,cAAyB;IAOzB,AADA,AADA,AADA,AADA,AADA,AADA,AADA,8CAAyB,0CACU,yBAClB,2CACkB,mCACR,6BACN,kCACK,6BACL;IAYvB,eAKC;IALD,4CAKC;;AD3BL;;;;;;;;;;;;;;GAcG;AAOH,MAAM,OAAO,qBAAsB,SAAQ,eAAe;IACxD,uCAAuC;IAC9B,KAAK,GAA2B,GAAG,CAAC;IAC7C,wCAAwC;IAC/B,MAAM,GAA2B,GAAG,CAAC;IAC9C,yDAAyD;IAChD,SAAS,GAAG,IAAI,CAAC;IAC1B,yCAAyC;IAChC,SAAS,GAAG,IAAI,CAAC;IAE1B,uEAAuE;IACvE,IAAuB,YAAY,KAAK,OAAO,kBAAkB,CAAC,CAAC,CAAC;iQAXzD,qBAAqB,yBAArB,qBAAqB;6DAArB,qBAAqB;YC7BlC,8FAAe;;YAAf,sCAqCC;4BDXW,YAAY,EAAE,eAAe,gCAAE,iBAAiB,EAAE,wBAAwB,EAAE,iBAAiB;;iFAG5F,qBAAqB;cANjC,SAAS;6BACI,IAAI,YACN,gBAAgB,WACjB,CAAC,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,iBAAiB,CAAC;;kBAKvG,KAAK;;kBAEL,KAAK;;kBAEL,KAAK;;kBAEL,KAAK;;kFARK,qBAAqB","sourcesContent":["import { Component, Input } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { MJWindowComponent, MJWindowActionsComponent, MJButtonDirective } from '@memberjunction/ng-ui-components';\n\nimport { BaseFormsModule } from '../../module';\nimport { BaseFormOverlay } from './base-form-overlay';\nimport { DIALOG_FORM_CONFIG } from '../types/entity-form-config';\n\n/**\n * Opens any MemberJunction entity form inside a floating, **non-modal** window\n * (draggable + resizable, no backdrop). Good for \"keep this open while I work\n * elsewhere\" scenarios — comparing records, reference-while-editing, etc.\n *\n * Same host + event surface as {@link MjFormDialogComponent}; only the chrome\n * differs (`mj-window` instead of `mj-dialog`). Defaults to\n * {@link DIALOG_FORM_CONFIG} (no in-form toolbar; the window footer owns\n * Save/Cancel).\n *\n * Standalone — import it directly:\n * ```typescript\n * import { MjFormWindowComponent } from '@memberjunction/ng-base-forms';\n * ```\n */\n@Component({\n standalone: true,\n selector: 'mj-form-window',\n imports: [CommonModule, BaseFormsModule, MJWindowComponent, MJWindowActionsComponent, MJButtonDirective],\n templateUrl: './form-window.component.html',\n})\nexport class MjFormWindowComponent extends BaseFormOverlay {\n /** Window width (px or CSS string). */\n @Input() Width: number | string | null = 760;\n /** Window height (px or CSS string). */\n @Input() Height: number | string | null = 560;\n /** Whether the window can be dragged by its titlebar. */\n @Input() Draggable = true;\n /** Whether the window can be resized. */\n @Input() Resizable = true;\n\n /** Form config. Defaults to the dialog preset (no in-form toolbar). */\n protected override get PresetConfig() { return DIALOG_FORM_CONFIG; }\n}\n","@if (Visible) {\n <mj-window\n [Visible]=\"true\"\n [Title]=\"effectiveTitle\"\n [Width]=\"Width\"\n [Height]=\"Height\"\n [Draggable]=\"Draggable\"\n [Resizable]=\"Resizable\"\n (Close)=\"onCancel()\">\n\n <mj-entity-form-host #host\n [EntityName]=\"EntityName\"\n [PrimaryKey]=\"effectivePrimaryKey!\"\n [Record]=\"Record\"\n [NewRecordValues]=\"NewRecordValues\"\n [SectionName]=\"SectionName\"\n [EditMode]=\"EditMode\"\n [Config]=\"EffectiveConfig\"\n [Provider]=\"Provider\"\n (Saved)=\"onHostSaved($event)\"\n (Dismissed)=\"onHostDismissed()\"\n (FormCreated)=\"onFormCreated($event)\"\n (Navigate)=\"Navigate.emit($event)\"\n (Notification)=\"Notification.emit($event)\"\n (RecordDeleted)=\"RecordDeleted.emit($event)\"\n (RecordSaveFailed)=\"RecordSaveFailed.emit($event)\"\n (ValidationFailed)=\"ValidationFailed.emit($event)\"\n (LoadError)=\"LoadError.emit($event)\">\n </mj-entity-form-host>\n\n @if (ShowFooter) {\n <mj-window-actions>\n <button mjButton variant=\"primary\" [disabled]=\"saving\" (click)=\"onSaveClick()\">{{ SaveButtonText }}</button>\n <button mjButton [disabled]=\"saving\" (click)=\"onCancel()\">{{ CancelButtonText }}</button>\n </mj-window-actions>\n }\n </mj-window>\n}\n"]}
|
|
@@ -67,6 +67,16 @@ export declare class MjCollapsiblePanelComponent implements OnInit, OnChanges, A
|
|
|
67
67
|
* (Shared PK in IS-A relationships.)
|
|
68
68
|
*/
|
|
69
69
|
InheritedRecordPrimaryKey?: CompositeKey;
|
|
70
|
+
/**
|
|
71
|
+
* Container-driven hard hide. When true the panel is removed from view
|
|
72
|
+
* regardless of search state — used by the record-form container to honor
|
|
73
|
+
* `EntityFormConfig.showRelatedEntities` / `hiddenSectionKeys` /
|
|
74
|
+
* `visibleSectionKeys`. Composes with (overrides) search visibility so all
|
|
75
|
+
* existing `IsVisible`-based section counts stay correct.
|
|
76
|
+
*/
|
|
77
|
+
set Hidden(value: boolean);
|
|
78
|
+
get Hidden(): boolean;
|
|
79
|
+
private _hidden;
|
|
70
80
|
/** @deprecated Use [SectionKey] instead */
|
|
71
81
|
set _deprecatedSectionKey(value: string);
|
|
72
82
|
/** @deprecated Use [SectionName] instead */
|
|
@@ -103,7 +113,14 @@ export declare class MjCollapsiblePanelComponent implements OnInit, OnChanges, A
|
|
|
103
113
|
get PanelContentHeight(): number | undefined;
|
|
104
114
|
/** Whether drag-to-reorder is allowed (from FormContext) */
|
|
105
115
|
get ReorderAllowed(): boolean;
|
|
106
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* Whether this panel's header may collapse/expand. Driven by
|
|
118
|
+
* `FormContext.collapsibleSections`; when false the panel renders
|
|
119
|
+
* always-expanded with no toggle chevron (used by dialog/slide-in surfaces
|
|
120
|
+
* that lock sections open). Undefined / true means collapsible.
|
|
121
|
+
*/
|
|
122
|
+
get Collapsible(): boolean;
|
|
123
|
+
/** Whether the panel is expanded (delegates to form state; always true when not collapsible) */
|
|
107
124
|
get Expanded(): boolean;
|
|
108
125
|
ngOnInit(): void;
|
|
109
126
|
ngAfterContentInit(): void;
|
|
@@ -140,6 +157,6 @@ export declare class MjCollapsiblePanelComponent implements OnInit, OnChanges, A
|
|
|
140
157
|
private DebouncePersistHeight;
|
|
141
158
|
private UpdateVisibilityAndHighlighting;
|
|
142
159
|
static ɵfac: i0.ɵɵFactoryDeclaration<MjCollapsiblePanelComponent, never>;
|
|
143
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<MjCollapsiblePanelComponent, "mj-collapsible-panel", never, { "SectionKey": { "alias": "SectionKey"; "required": false; }; "SectionName": { "alias": "SectionName"; "required": false; }; "Icon": { "alias": "Icon"; "required": false; }; "Form": { "alias": "Form"; "required": false; }; "FormContext": { "alias": "FormContext"; "required": false; }; "Variant": { "alias": "Variant"; "required": false; }; "BadgeCount": { "alias": "BadgeCount"; "required": false; }; "DefaultExpanded": { "alias": "DefaultExpanded"; "required": false; }; "InheritedFromEntity": { "alias": "InheritedFromEntity"; "required": false; }; "InheritedRecordPrimaryKey": { "alias": "InheritedRecordPrimaryKey"; "required": false; }; "_deprecatedSectionKey": { "alias": "sectionKey"; "required": false; }; "_deprecatedSectionName": { "alias": "sectionName"; "required": false; }; "_deprecatedIcon": { "alias": "icon"; "required": false; }; "_deprecatedForm": { "alias": "form"; "required": false; }; "_deprecatedFormContext": { "alias": "formContext"; "required": false; }; }, { "DragStarted": "DragStarted"; "DragEnded": "DragEnded"; "PanelDrop": "PanelDrop"; "Navigate": "Navigate"; }, ["FieldComponents"], ["*"], false, never>;
|
|
160
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<MjCollapsiblePanelComponent, "mj-collapsible-panel", never, { "SectionKey": { "alias": "SectionKey"; "required": false; }; "SectionName": { "alias": "SectionName"; "required": false; }; "Icon": { "alias": "Icon"; "required": false; }; "Form": { "alias": "Form"; "required": false; }; "FormContext": { "alias": "FormContext"; "required": false; }; "Variant": { "alias": "Variant"; "required": false; }; "BadgeCount": { "alias": "BadgeCount"; "required": false; }; "DefaultExpanded": { "alias": "DefaultExpanded"; "required": false; }; "InheritedFromEntity": { "alias": "InheritedFromEntity"; "required": false; }; "InheritedRecordPrimaryKey": { "alias": "InheritedRecordPrimaryKey"; "required": false; }; "Hidden": { "alias": "Hidden"; "required": false; }; "_deprecatedSectionKey": { "alias": "sectionKey"; "required": false; }; "_deprecatedSectionName": { "alias": "sectionName"; "required": false; }; "_deprecatedIcon": { "alias": "icon"; "required": false; }; "_deprecatedForm": { "alias": "form"; "required": false; }; "_deprecatedFormContext": { "alias": "formContext"; "required": false; }; }, { "DragStarted": "DragStarted"; "DragEnded": "DragEnded"; "PanelDrop": "PanelDrop"; "Navigate": "Navigate"; }, ["FieldComponents"], ["*"], false, never>;
|
|
144
161
|
}
|
|
145
162
|
//# sourceMappingURL=collapsible-panel.component.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collapsible-panel.component.d.ts","sourceRoot":"","sources":["../../../src/lib/panel/collapsible-panel.component.ts"],"names":[],"mappings":"AAAA,OAAO,EACqB,YAAY,EAEtC,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,gBAAgB,EAAE,aAAa,EAAE,SAAS,EAC3D,SAAS,EAE3B,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"collapsible-panel.component.d.ts","sourceRoot":"","sources":["../../../src/lib/panel/collapsible-panel.component.ts"],"names":[],"mappings":"AAAA,OAAO,EACqB,YAAY,EAEtC,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,gBAAgB,EAAE,aAAa,EAAE,SAAS,EAC3D,SAAS,EAE3B,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErG,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;;AAGpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAQa,2BAA4B,YAAW,MAAM,EAAE,SAAS,EAAE,gBAAgB,EAAE,aAAa,EAAE,SAAS;IAC/G,OAAO,CAAC,GAAG,CAA6B;IACxC,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,MAAM,CAAkB;IAEhC,uCAAuC;IAC9B,UAAU,SAAM;IAEzB,6CAA6C;IACpC,WAAW,SAAM;IAE1B,mDAAmD;IAC1C,IAAI,SAAkB;IAE/B;;;OAGG;IACM,IAAI,EAAE,OAAO,CAAC;IAEvB,0DAA0D;IACjD,WAAW,CAAC,EAAE,WAAW,CAAC;IAEnC,2BAA2B;IAClB,OAAO,EAAE,YAAY,CAAa;IAE3C,kDAAkD;IACzC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAExC,4DAA4D;IACnD,eAAe,EAAE,OAAO,GAAG,SAAS,CAAC;IAE9C;;;OAGG;IACM,mBAAmB,SAAM;IAElC;;;OAGG;IACM,yBAAyB,CAAC,EAAE,YAAY,CAAC;IAElD;;;;;;OAMG;IACH,IACI,MAAM,CAAC,KAAK,EAAE,OAAO,EAQxB;IACD,IAAI,MAAM,IAAI,OAAO,CAEpB;IACD,OAAO,CAAC,OAAO,CAAS;IAIxB,2CAA2C;IAC3C,IAAyB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAA8B;IAE1F,4CAA4C;IAC5C,IAA0B,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAA+B;IAE7F,qCAAqC;IACrC,IAAmB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAwB;IAExE,qCAAqC;IACrC,IAAmB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAwB;IAEzE,4CAA4C;IAC5C,IAA0B,sBAAsB,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAA+B;IAIpG,WAAW,oCAA2C;IACtD,SAAS,qBAA4B;IACrC,SAAS,+BAAsC;IAC/C,QAAQ,oCAA2C;IAIC,eAAe,EAAG,SAAS,CAAC,oBAAoB,CAAC,CAAC;IAIhH,WAAW,SAAM;IACjB,UAAU,SAAM;IAChB,SAAS,UAAQ;IAEjB,IACI,SAAS,IAAI,MAAM,CAMtB;IAED,IACI,QAAQ,IAAI,MAAM,CAGrB;IAED,UAAU,UAAS;IACnB,UAAU,UAAS;IAInB,0DAA0D;IAC1D,OAAO,CAAC,cAAc,CAAuB;IAIlB,OAAO,CAAC,eAAe,CAAC,CAA0B;IAC7E,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,mBAAmB,CAAC,CAAgC;IAC5D,2GAA2G;IAC3G,OAAO,CAAC,oBAAoB,CAAS;IAErC;;;OAGG;IACH,IAAI,kBAAkB,IAAI,MAAM,GAAG,SAAS,CAI3C;IAED,4DAA4D;IAC5D,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED;;;;;OAKG;IACH,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,gGAAgG;IAChG,IAAI,QAAQ,IAAI,OAAO,CAItB;IAID,QAAQ,IAAI,IAAI;IAIhB,kBAAkB,IAAI,IAAI;IAS1B,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAczC,eAAe,IAAI,IAAI;IAIvB,WAAW,IAAI,IAAI;IAWnB,MAAM,IAAI,IAAI;IASd;;;;OAIG;IACH,qBAAqB,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAmB9C,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAUlC,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAOnC,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAgB9B,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAUnC,SAAS,IAAI,IAAI;IAOjB,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,gBAAgB;IAaxB;;;OAGG;IACH,OAAO,CAAC,8BAA8B;IAStC;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IA0B3B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,+BAA+B;yCA1X5B,2BAA2B;2CAA3B,2BAA2B;CA2ZvC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ChangeDetectorRef, inject, ContentChildren, HostBinding, HostListener, ElementRef, ViewChild, NgZone, ViewEncapsulation } from '@angular/core';
|
|
2
2
|
import { Subject } from 'rxjs';
|
|
3
3
|
import { takeUntil } from 'rxjs/operators';
|
|
4
|
+
import { IsFormSectionHidden } from '../types/entity-form-config';
|
|
4
5
|
import { MjFormFieldComponent } from '../field/form-field.component';
|
|
5
6
|
import { CompositeKey } from '@memberjunction/core';
|
|
6
7
|
import { EscapeHTML, HighlightSearchMatches } from '@memberjunction/global';
|
|
@@ -37,6 +38,15 @@ function MjCollapsiblePanelComponent_Conditional_8_Template(rf, ctx) { if (rf &
|
|
|
37
38
|
i0.ɵɵadvance(2);
|
|
38
39
|
i0.ɵɵtextInterpolate1(" Inherited from ", ctx_r2.InheritedFromEntity, " ");
|
|
39
40
|
} }
|
|
41
|
+
function MjCollapsiblePanelComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
42
|
+
i0.ɵɵelementStart(0, "div", 10);
|
|
43
|
+
i0.ɵɵelement(1, "i");
|
|
44
|
+
i0.ɵɵelementEnd();
|
|
45
|
+
} if (rf & 2) {
|
|
46
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
47
|
+
i0.ɵɵadvance();
|
|
48
|
+
i0.ɵɵclassMap(ctx_r2.Expanded ? "fa fa-chevron-up" : "fa fa-chevron-down");
|
|
49
|
+
} }
|
|
40
50
|
/**
|
|
41
51
|
* Reusable collapsible panel for form sections.
|
|
42
52
|
*
|
|
@@ -100,6 +110,26 @@ export class MjCollapsiblePanelComponent {
|
|
|
100
110
|
* (Shared PK in IS-A relationships.)
|
|
101
111
|
*/
|
|
102
112
|
InheritedRecordPrimaryKey;
|
|
113
|
+
/**
|
|
114
|
+
* Container-driven hard hide. When true the panel is removed from view
|
|
115
|
+
* regardless of search state — used by the record-form container to honor
|
|
116
|
+
* `EntityFormConfig.showRelatedEntities` / `hiddenSectionKeys` /
|
|
117
|
+
* `visibleSectionKeys`. Composes with (overrides) search visibility so all
|
|
118
|
+
* existing `IsVisible`-based section counts stay correct.
|
|
119
|
+
*/
|
|
120
|
+
set Hidden(value) {
|
|
121
|
+
if (value !== this._hidden) {
|
|
122
|
+
this._hidden = value;
|
|
123
|
+
// Recompute visibility if content has initialized (FieldComponents present).
|
|
124
|
+
if (this.FieldComponents) {
|
|
125
|
+
this.UpdateVisibilityAndHighlighting();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
get Hidden() {
|
|
130
|
+
return this._hidden;
|
|
131
|
+
}
|
|
132
|
+
_hidden = false;
|
|
103
133
|
// ---- Deprecated camelCase aliases (backward compat) ----
|
|
104
134
|
/** @deprecated Use [SectionKey] instead */
|
|
105
135
|
set _deprecatedSectionKey(value) { this.SectionKey = value; }
|
|
@@ -161,8 +191,19 @@ export class MjCollapsiblePanelComponent {
|
|
|
161
191
|
get ReorderAllowed() {
|
|
162
192
|
return this.FormContext?.allowSectionReorder !== false;
|
|
163
193
|
}
|
|
164
|
-
/**
|
|
194
|
+
/**
|
|
195
|
+
* Whether this panel's header may collapse/expand. Driven by
|
|
196
|
+
* `FormContext.collapsibleSections`; when false the panel renders
|
|
197
|
+
* always-expanded with no toggle chevron (used by dialog/slide-in surfaces
|
|
198
|
+
* that lock sections open). Undefined / true means collapsible.
|
|
199
|
+
*/
|
|
200
|
+
get Collapsible() {
|
|
201
|
+
return this.FormContext?.collapsibleSections !== false;
|
|
202
|
+
}
|
|
203
|
+
/** Whether the panel is expanded (delegates to form state; always true when not collapsible) */
|
|
165
204
|
get Expanded() {
|
|
205
|
+
if (!this.Collapsible)
|
|
206
|
+
return true;
|
|
166
207
|
const formRef = this.Form;
|
|
167
208
|
return formRef?.IsSectionExpanded ? formRef.IsSectionExpanded(this.SectionKey, this.DefaultExpanded) : true;
|
|
168
209
|
}
|
|
@@ -204,6 +245,8 @@ export class MjCollapsiblePanelComponent {
|
|
|
204
245
|
}
|
|
205
246
|
// ---- Actions ----
|
|
206
247
|
Toggle() {
|
|
248
|
+
if (!this.Collapsible)
|
|
249
|
+
return;
|
|
207
250
|
const formRef = this.Form;
|
|
208
251
|
if (formRef?.SetSectionExpanded) {
|
|
209
252
|
formRef.SetSectionExpanded(this.SectionKey, !this.Expanded);
|
|
@@ -358,6 +401,16 @@ export class MjCollapsiblePanelComponent {
|
|
|
358
401
|
}, 500);
|
|
359
402
|
}
|
|
360
403
|
UpdateVisibilityAndHighlighting() {
|
|
404
|
+
// Hard hide takes precedence over search state. Driven by an explicit
|
|
405
|
+
// `Hidden` input OR the form config's section-visibility rules carried on
|
|
406
|
+
// FormContext (which also reach slot-injected BaseFormPanels, since every
|
|
407
|
+
// panel receives FormContext).
|
|
408
|
+
if (this._hidden || IsFormSectionHidden(this.FormContext, this.SectionKey, this.Variant)) {
|
|
409
|
+
this.IsVisible = false;
|
|
410
|
+
this.DisplayName = EscapeHTML(this.SectionName);
|
|
411
|
+
this.cdr.markForCheck();
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
361
414
|
const searchTerm = (this.FormContext?.sectionFilter || '').toLowerCase().trim();
|
|
362
415
|
if (!searchTerm) {
|
|
363
416
|
this.IsVisible = true;
|
|
@@ -391,7 +444,7 @@ export class MjCollapsiblePanelComponent {
|
|
|
391
444
|
} if (rf & 2) {
|
|
392
445
|
i0.ɵɵclassMap(ctx.HostClass);
|
|
393
446
|
i0.ɵɵstyleProp("order", ctx.CssOrder);
|
|
394
|
-
} }, inputs: { SectionKey: "SectionKey", SectionName: "SectionName", Icon: "Icon", Form: "Form", FormContext: "FormContext", Variant: "Variant", BadgeCount: "BadgeCount", DefaultExpanded: "DefaultExpanded", InheritedFromEntity: "InheritedFromEntity", InheritedRecordPrimaryKey: "InheritedRecordPrimaryKey", _deprecatedSectionKey: [0, "sectionKey", "_deprecatedSectionKey"], _deprecatedSectionName: [0, "sectionName", "_deprecatedSectionName"], _deprecatedIcon: [0, "icon", "_deprecatedIcon"], _deprecatedForm: [0, "form", "_deprecatedForm"], _deprecatedFormContext: [0, "formContext", "_deprecatedFormContext"] }, outputs: { DragStarted: "DragStarted", DragEnded: "DragEnded", PanelDrop: "PanelDrop", Navigate: "Navigate" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], ngContentSelectors: _c1, decls:
|
|
447
|
+
} }, inputs: { SectionKey: "SectionKey", SectionName: "SectionName", Icon: "Icon", Form: "Form", FormContext: "FormContext", Variant: "Variant", BadgeCount: "BadgeCount", DefaultExpanded: "DefaultExpanded", InheritedFromEntity: "InheritedFromEntity", InheritedRecordPrimaryKey: "InheritedRecordPrimaryKey", Hidden: "Hidden", _deprecatedSectionKey: [0, "sectionKey", "_deprecatedSectionKey"], _deprecatedSectionName: [0, "sectionName", "_deprecatedSectionName"], _deprecatedIcon: [0, "icon", "_deprecatedIcon"], _deprecatedForm: [0, "form", "_deprecatedForm"], _deprecatedFormContext: [0, "formContext", "_deprecatedFormContext"] }, outputs: { DragStarted: "DragStarted", DragEnded: "DragEnded", PanelDrop: "PanelDrop", Navigate: "Navigate" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], ngContentSelectors: _c1, decls: 14, vars: 20, consts: [["panelContent", ""], [1, "mj-forms-panel"], [1, "mj-forms-panel-header", 3, "click"], ["title", "Drag to reorder sections", "draggable", "true", 1, "mj-forms-drag-handle"], [1, "mj-forms-panel-title-group"], [1, "mj-forms-panel-icon"], [1, "mj-forms-panel-title"], [3, "innerHTML"], [1, "mj-forms-row-count-badge", 3, "mj-forms-row-count-badge--zero"], [1, "mj-forms-inherited-badge", 3, "title"], [1, "mj-forms-panel-chevron"], [1, "mj-forms-panel-body"], [1, "mj-forms-panel-content"], ["title", "Drag to reorder sections", "draggable", "true", 1, "mj-forms-drag-handle", 3, "dragstart", "dragend", "click"], [1, "fa-solid", "fa-grip-vertical"], [1, "mj-forms-row-count-badge"], [1, "mj-forms-inherited-badge", 3, "click", "title"], [1, "fa-solid", "fa-arrow-up"]], template: function MjCollapsiblePanelComponent_Template(rf, ctx) { if (rf & 1) {
|
|
395
448
|
const _r1 = i0.ɵɵgetCurrentView();
|
|
396
449
|
i0.ɵɵprojectionDef();
|
|
397
450
|
i0.ɵɵelementStart(0, "div", 1)(1, "div", 2);
|
|
@@ -404,16 +457,18 @@ export class MjCollapsiblePanelComponent {
|
|
|
404
457
|
i0.ɵɵconditionalCreate(7, MjCollapsiblePanelComponent_Conditional_7_Template, 2, 3, "span", 8);
|
|
405
458
|
i0.ɵɵelementEnd()();
|
|
406
459
|
i0.ɵɵconditionalCreate(8, MjCollapsiblePanelComponent_Conditional_8_Template, 3, 2, "a", 9);
|
|
407
|
-
i0.ɵɵ
|
|
408
|
-
i0.ɵɵ
|
|
409
|
-
i0.ɵɵ
|
|
410
|
-
i0.ɵɵ
|
|
411
|
-
i0.ɵɵprojection(14);
|
|
460
|
+
i0.ɵɵconditionalCreate(9, MjCollapsiblePanelComponent_Conditional_9_Template, 2, 2, "div", 10);
|
|
461
|
+
i0.ɵɵelementEnd();
|
|
462
|
+
i0.ɵɵelementStart(10, "div", 11)(11, "div", 12, 0);
|
|
463
|
+
i0.ɵɵprojection(13);
|
|
412
464
|
i0.ɵɵelementEnd()()();
|
|
413
465
|
} if (rf & 2) {
|
|
414
466
|
i0.ɵɵclassProp("mj-forms-panel--related", ctx.Variant === "related-entity")("mj-forms-panel--inherited", ctx.Variant === "inherited");
|
|
415
467
|
i0.ɵɵattribute("data-section-key", ctx.SectionKey);
|
|
416
|
-
i0.ɵɵadvance(
|
|
468
|
+
i0.ɵɵadvance();
|
|
469
|
+
i0.ɵɵclassProp("mj-forms-panel-header--static", !ctx.Collapsible);
|
|
470
|
+
i0.ɵɵattribute("role", ctx.Collapsible ? "button" : null)("tabindex", ctx.Collapsible ? 0 : null);
|
|
471
|
+
i0.ɵɵadvance();
|
|
417
472
|
i0.ɵɵconditional(ctx.ReorderAllowed ? 2 : -1);
|
|
418
473
|
i0.ɵɵadvance(2);
|
|
419
474
|
i0.ɵɵclassMap(ctx.Icon);
|
|
@@ -423,17 +478,17 @@ export class MjCollapsiblePanelComponent {
|
|
|
423
478
|
i0.ɵɵconditional(ctx.BadgeCount !== undefined ? 7 : -1);
|
|
424
479
|
i0.ɵɵadvance();
|
|
425
480
|
i0.ɵɵconditional(ctx.Variant === "inherited" && ctx.InheritedFromEntity ? 8 : -1);
|
|
426
|
-
i0.ɵɵadvance(
|
|
427
|
-
i0.ɵɵ
|
|
481
|
+
i0.ɵɵadvance();
|
|
482
|
+
i0.ɵɵconditional(ctx.Collapsible ? 9 : -1);
|
|
428
483
|
i0.ɵɵadvance();
|
|
429
484
|
i0.ɵɵclassProp("mj-forms-panel-body--collapsed", !ctx.Expanded);
|
|
430
485
|
i0.ɵɵadvance();
|
|
431
486
|
i0.ɵɵstyleProp("height", ctx.PanelContentHeight, "px");
|
|
432
|
-
} }, styles: ["mj-collapsible-panel {\n display: block;\n}\n\nmj-collapsible-panel.mj-search-hidden {\n display: none;\n}\n\nmj-collapsible-panel.mj-drag-over {\n border: 2px dashed var(--mj-brand-primary);\n border-radius: var(--mj-radius-md);\n}\n\n/* ============================================\n PANEL CARD\n ============================================ */\n.mj-forms-panel {\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-sm);\n border: 1px solid var(--mj-border-default);\n overflow: hidden;\n transition: all 0.3s ease;\n}\n\n/* ============================================\n HEADER\n ============================================ */\n.mj-forms-panel-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 20px;\n background: var(--mj-bg-surface-elevated);\n border-bottom: 2px solid var(--mj-border-default);\n cursor: pointer;\n user-select: none;\n transition: all 0.3s ease;\n}\n\n.mj-forms-panel-header:hover {\n background: var(--mj-bg-surface-hover);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n/* Drag handle */\n.mj-forms-drag-handle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 0;\n height: 24px;\n color: var(--mj-text-muted);\n cursor: grab;\n opacity: 0;\n overflow: hidden;\n transition: width var(--mj-transition-base) ease, opacity var(--mj-transition-base) ease;\n flex-shrink: 0;\n}\n\n.mj-forms-panel-header:hover .mj-forms-drag-handle {\n width: 24px;\n opacity: 1;\n}\n\n.mj-forms-drag-handle:hover {\n color: var(--mj-brand-primary);\n}\n\n.mj-forms-drag-handle:active {\n cursor: grabbing;\n}\n\nmj-collapsible-panel.mj-dragging .mj-forms-drag-handle {\n width: 24px;\n opacity: 1;\n color: var(--mj-brand-primary);\n}\n\n/* Title group */\n.mj-forms-panel-title-group {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n}\n\n.mj-forms-panel-icon {\n font-size: 18px;\n color: var(--mj-brand-primary);\n width: 24px;\n text-align: center;\n}\n\n.mj-forms-panel-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mj-forms-panel-chevron {\n color: var(--mj-text-secondary);\n font-size: 14px;\n transition: transform 0.3s ease;\n padding: 4px;\n}\n\n/* Row count badge */\n.mj-forms-row-count-badge {\n background: var(--mj-status-success);\n color: var(--mj-text-inverse);\n padding: 2px 8px;\n border-radius: var(--mj-radius-full);\n font-size: 12px;\n font-weight: 600;\n margin-left: 8px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n line-height: 1;\n}\n\n.mj-forms-row-count-badge--zero {\n background: var(--mj-text-muted);\n}\n\n/* ============================================\n INHERITED VARIANT (IS-A parent fields)\n ============================================ */\n.mj-forms-panel--inherited {\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-header {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface-elevated));\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-header:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface-elevated));\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-icon {\n color: var(--mj-brand-primary);\n}\n\n.mj-forms-inherited-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n font-weight: 600;\n padding: 3px 10px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n cursor: pointer;\n transition: all var(--mj-transition-base) ease;\n text-decoration: none;\n}\n\n.mj-forms-inherited-badge:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 18%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 6px color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n}\n\n.mj-forms-inherited-badge i {\n font-size: 10px;\n}\n\n/* ============================================\n RELATED ENTITY VARIANT\n ============================================ */\n.mj-forms-panel--related {\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.mj-forms-panel--related .mj-forms-panel-header {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface-elevated));\n}\n\n.mj-forms-panel--related .mj-forms-panel-header:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface-elevated));\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.mj-forms-panel--related .mj-forms-panel-icon {\n color: var(--mj-brand-primary);\n}\n\n/* Related entity content \u2014 400px default, user can drag-resize vertically */\n.mj-forms-panel--related .mj-forms-panel-content {\n height: 400px;\n padding: 0;\n overflow: hidden;\n resize: vertical;\n min-height: 120px;\n max-height: 800px;\n}\n\n/* Ensure child chain fills the resizable content area */\n.mj-forms-panel--related .mj-forms-panel-content > *,\n.mj-forms-panel--related .mj-forms-panel-content > .ng-star-inserted {\n height: 100%;\n}\n\n.mj-forms-panel--related .mj-forms-panel-content > * > * {\n height: 100%;\n}\n\n/* Related entity panels need the body to not clip the resize handle */\n.mj-forms-panel--related .mj-forms-panel-body {\n overflow: visible;\n}\n\n/* ============================================\n BODY (collapsible)\n ============================================ */\n.mj-forms-panel-body {\n max-height: 2000px;\n overflow: hidden;\n transition: max-height 0.4s ease, padding 0.4s ease, opacity 0.3s ease;\n opacity: 1;\n}\n\n.mj-forms-panel-body--collapsed {\n max-height: 0;\n padding: 0;\n opacity: 0;\n}\n\n.mj-forms-panel-content {\n padding: 20px;\n overflow: auto;\n max-height: 600px;\n}\n\n/* ============================================\n SEARCH HIGHLIGHTING\n ============================================ */\n.mj-forms-panel-title ::ng-deep .mj-forms-search-highlight {\n background-color: var(--mj-
|
|
487
|
+
} }, styles: ["mj-collapsible-panel {\n display: block;\n}\n\nmj-collapsible-panel.mj-search-hidden {\n display: none;\n}\n\nmj-collapsible-panel.mj-drag-over {\n border: 2px dashed var(--mj-brand-primary);\n border-radius: var(--mj-radius-md);\n}\n\n/* ============================================\n PANEL CARD\n ============================================ */\n.mj-forms-panel {\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-sm);\n border: 1px solid var(--mj-border-default);\n overflow: hidden;\n transition: all 0.3s ease;\n}\n\n/* ============================================\n HEADER\n ============================================ */\n.mj-forms-panel-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 20px;\n background: var(--mj-bg-surface-elevated);\n border-bottom: 2px solid var(--mj-border-default);\n cursor: pointer;\n user-select: none;\n transition: all 0.3s ease;\n}\n\n.mj-forms-panel-header:hover {\n background: var(--mj-bg-surface-hover);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n/* Static header \u2014 sections locked open (Config.collapsibleSections === false) */\n.mj-forms-panel-header--static {\n cursor: default;\n}\n\n.mj-forms-panel-header--static:hover {\n background: var(--mj-bg-surface-elevated);\n border-bottom-color: var(--mj-border-default);\n}\n\n/* Drag handle */\n.mj-forms-drag-handle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 0;\n height: 24px;\n color: var(--mj-text-muted);\n cursor: grab;\n opacity: 0;\n overflow: hidden;\n transition: width var(--mj-transition-base) ease, opacity var(--mj-transition-base) ease;\n flex-shrink: 0;\n}\n\n.mj-forms-panel-header:hover .mj-forms-drag-handle {\n width: 24px;\n opacity: 1;\n}\n\n.mj-forms-drag-handle:hover {\n color: var(--mj-brand-primary);\n}\n\n.mj-forms-drag-handle:active {\n cursor: grabbing;\n}\n\nmj-collapsible-panel.mj-dragging .mj-forms-drag-handle {\n width: 24px;\n opacity: 1;\n color: var(--mj-brand-primary);\n}\n\n/* Title group */\n.mj-forms-panel-title-group {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n}\n\n.mj-forms-panel-icon {\n font-size: 18px;\n color: var(--mj-brand-primary);\n width: 24px;\n text-align: center;\n}\n\n.mj-forms-panel-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mj-forms-panel-chevron {\n color: var(--mj-text-secondary);\n font-size: 14px;\n transition: transform 0.3s ease;\n padding: 4px;\n}\n\n/* Row count badge */\n.mj-forms-row-count-badge {\n background: var(--mj-status-success);\n color: var(--mj-text-inverse);\n padding: 2px 8px;\n border-radius: var(--mj-radius-full);\n font-size: 12px;\n font-weight: 600;\n margin-left: 8px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n line-height: 1;\n}\n\n.mj-forms-row-count-badge--zero {\n background: var(--mj-text-muted);\n}\n\n/* ============================================\n INHERITED VARIANT (IS-A parent fields)\n ============================================ */\n.mj-forms-panel--inherited {\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-header {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface-elevated));\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-header:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface-elevated));\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-icon {\n color: var(--mj-brand-primary);\n}\n\n.mj-forms-inherited-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n font-weight: 600;\n padding: 3px 10px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n cursor: pointer;\n transition: all var(--mj-transition-base) ease;\n text-decoration: none;\n}\n\n.mj-forms-inherited-badge:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 18%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 6px color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n}\n\n.mj-forms-inherited-badge i {\n font-size: 10px;\n}\n\n/* ============================================\n RELATED ENTITY VARIANT\n ============================================ */\n.mj-forms-panel--related {\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.mj-forms-panel--related .mj-forms-panel-header {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface-elevated));\n}\n\n.mj-forms-panel--related .mj-forms-panel-header:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface-elevated));\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.mj-forms-panel--related .mj-forms-panel-icon {\n color: var(--mj-brand-primary);\n}\n\n/* Related entity content \u2014 400px default, user can drag-resize vertically */\n.mj-forms-panel--related .mj-forms-panel-content {\n height: 400px;\n padding: 0;\n overflow: hidden;\n resize: vertical;\n min-height: 120px;\n max-height: 800px;\n}\n\n/* Ensure child chain fills the resizable content area */\n.mj-forms-panel--related .mj-forms-panel-content > *,\n.mj-forms-panel--related .mj-forms-panel-content > .ng-star-inserted {\n height: 100%;\n}\n\n.mj-forms-panel--related .mj-forms-panel-content > * > * {\n height: 100%;\n}\n\n/* Related entity panels need the body to not clip the resize handle */\n.mj-forms-panel--related .mj-forms-panel-body {\n overflow: visible;\n}\n\n/* ============================================\n BODY (collapsible)\n ============================================ */\n.mj-forms-panel-body {\n max-height: 2000px;\n overflow: hidden;\n transition: max-height 0.4s ease, padding 0.4s ease, opacity 0.3s ease;\n opacity: 1;\n}\n\n.mj-forms-panel-body--collapsed {\n max-height: 0;\n padding: 0;\n opacity: 0;\n}\n\n.mj-forms-panel-content {\n padding: 20px;\n overflow: auto;\n max-height: 600px;\n}\n\n/* ============================================\n SEARCH HIGHLIGHTING\n ============================================ */\n.mj-forms-panel-title ::ng-deep .mj-forms-search-highlight {\n /* Muted, theme-aware highlight (semantic token) \u2014 consistent with the field\n label + FK dropdown highlights. Replaces the old bright-yellow primitive. */\n background-color: var(--mj-search-highlight-bg);\n color: var(--mj-search-highlight-text);\n border-radius: 2px;\n padding: 0 1px;\n font-weight: 600;\n}\n"], encapsulation: 2, changeDetection: 0 });
|
|
433
488
|
}
|
|
434
489
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MjCollapsiblePanelComponent, [{
|
|
435
490
|
type: Component,
|
|
436
|
-
args: [{ standalone: false, selector: 'mj-collapsible-panel', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"mj-forms-panel\"\n [class.mj-forms-panel--related]=\"Variant === 'related-entity'\"\n [class.mj-forms-panel--inherited]=\"Variant === 'inherited'\"\n [attr.data-section-key]=\"SectionKey\">\n\n <!-- Panel Header -->\n <div class=\"mj-forms-panel-header\"
|
|
491
|
+
args: [{ standalone: false, selector: 'mj-collapsible-panel', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"mj-forms-panel\"\n [class.mj-forms-panel--related]=\"Variant === 'related-entity'\"\n [class.mj-forms-panel--inherited]=\"Variant === 'inherited'\"\n [attr.data-section-key]=\"SectionKey\">\n\n <!-- Panel Header -->\n <div class=\"mj-forms-panel-header\"\n [class.mj-forms-panel-header--static]=\"!Collapsible\"\n [attr.role]=\"Collapsible ? 'button' : null\"\n [attr.tabindex]=\"Collapsible ? 0 : null\"\n (click)=\"Toggle()\">\n <!-- Drag Handle (hidden when reorder disabled) -->\n @if (ReorderAllowed) {\n <div class=\"mj-forms-drag-handle\"\n title=\"Drag to reorder sections\"\n draggable=\"true\"\n (dragstart)=\"OnDragStart($event)\"\n (dragend)=\"OnDragEnd()\"\n (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-grip-vertical\"></i>\n </div>\n }\n\n <!-- Icon + Title -->\n <div class=\"mj-forms-panel-title-group\">\n <i [class]=\"Icon\" class=\"mj-forms-panel-icon\"></i>\n <h3 class=\"mj-forms-panel-title\">\n <span [innerHTML]=\"DisplayName\"></span>\n <!-- Row count badge for related entities -->\n @if (BadgeCount !== undefined) {\n <span class=\"mj-forms-row-count-badge\" [class.mj-forms-row-count-badge--zero]=\"BadgeCount === 0\">\n {{ BadgeCount }}\n </span>\n }\n </h3>\n </div>\n\n <!-- Inherited From badge (IS-A) -->\n @if (Variant === 'inherited' && InheritedFromEntity) {\n <a class=\"mj-forms-inherited-badge\" (click)=\"OnInheritedBadgeClick($event)\"\n [title]=\"'View parent record: ' + InheritedFromEntity\">\n <i class=\"fa-solid fa-arrow-up\"></i>\n Inherited from {{ InheritedFromEntity }}\n </a>\n }\n\n <!-- Chevron (hidden when sections are locked open) -->\n @if (Collapsible) {\n <div class=\"mj-forms-panel-chevron\">\n <i [class]=\"Expanded ? 'fa fa-chevron-up' : 'fa fa-chevron-down'\"></i>\n </div>\n }\n </div>\n\n <!-- Panel Body -->\n <div class=\"mj-forms-panel-body\" [class.mj-forms-panel-body--collapsed]=\"!Expanded\">\n <div class=\"mj-forms-panel-content\"\n #panelContent\n [style.height.px]=\"PanelContentHeight\">\n <ng-content></ng-content>\n </div>\n </div>\n</div>\n", styles: ["mj-collapsible-panel {\n display: block;\n}\n\nmj-collapsible-panel.mj-search-hidden {\n display: none;\n}\n\nmj-collapsible-panel.mj-drag-over {\n border: 2px dashed var(--mj-brand-primary);\n border-radius: var(--mj-radius-md);\n}\n\n/* ============================================\n PANEL CARD\n ============================================ */\n.mj-forms-panel {\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-sm);\n border: 1px solid var(--mj-border-default);\n overflow: hidden;\n transition: all 0.3s ease;\n}\n\n/* ============================================\n HEADER\n ============================================ */\n.mj-forms-panel-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 20px;\n background: var(--mj-bg-surface-elevated);\n border-bottom: 2px solid var(--mj-border-default);\n cursor: pointer;\n user-select: none;\n transition: all 0.3s ease;\n}\n\n.mj-forms-panel-header:hover {\n background: var(--mj-bg-surface-hover);\n border-bottom-color: var(--mj-brand-primary);\n}\n\n/* Static header \u2014 sections locked open (Config.collapsibleSections === false) */\n.mj-forms-panel-header--static {\n cursor: default;\n}\n\n.mj-forms-panel-header--static:hover {\n background: var(--mj-bg-surface-elevated);\n border-bottom-color: var(--mj-border-default);\n}\n\n/* Drag handle */\n.mj-forms-drag-handle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 0;\n height: 24px;\n color: var(--mj-text-muted);\n cursor: grab;\n opacity: 0;\n overflow: hidden;\n transition: width var(--mj-transition-base) ease, opacity var(--mj-transition-base) ease;\n flex-shrink: 0;\n}\n\n.mj-forms-panel-header:hover .mj-forms-drag-handle {\n width: 24px;\n opacity: 1;\n}\n\n.mj-forms-drag-handle:hover {\n color: var(--mj-brand-primary);\n}\n\n.mj-forms-drag-handle:active {\n cursor: grabbing;\n}\n\nmj-collapsible-panel.mj-dragging .mj-forms-drag-handle {\n width: 24px;\n opacity: 1;\n color: var(--mj-brand-primary);\n}\n\n/* Title group */\n.mj-forms-panel-title-group {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n}\n\n.mj-forms-panel-icon {\n font-size: 18px;\n color: var(--mj-brand-primary);\n width: 24px;\n text-align: center;\n}\n\n.mj-forms-panel-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.mj-forms-panel-chevron {\n color: var(--mj-text-secondary);\n font-size: 14px;\n transition: transform 0.3s ease;\n padding: 4px;\n}\n\n/* Row count badge */\n.mj-forms-row-count-badge {\n background: var(--mj-status-success);\n color: var(--mj-text-inverse);\n padding: 2px 8px;\n border-radius: var(--mj-radius-full);\n font-size: 12px;\n font-weight: 600;\n margin-left: 8px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 24px;\n line-height: 1;\n}\n\n.mj-forms-row-count-badge--zero {\n background: var(--mj-text-muted);\n}\n\n/* ============================================\n INHERITED VARIANT (IS-A parent fields)\n ============================================ */\n.mj-forms-panel--inherited {\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-header {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface-elevated));\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-header:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface-elevated));\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.mj-forms-panel--inherited .mj-forms-panel-icon {\n color: var(--mj-brand-primary);\n}\n\n.mj-forms-inherited-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n font-weight: 600;\n padding: 3px 10px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n cursor: pointer;\n transition: all var(--mj-transition-base) ease;\n text-decoration: none;\n}\n\n.mj-forms-inherited-badge:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 18%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 6px color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n}\n\n.mj-forms-inherited-badge i {\n font-size: 10px;\n}\n\n/* ============================================\n RELATED ENTITY VARIANT\n ============================================ */\n.mj-forms-panel--related {\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.mj-forms-panel--related .mj-forms-panel-header {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface-elevated));\n}\n\n.mj-forms-panel--related .mj-forms-panel-header:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface-elevated));\n border-bottom-color: var(--mj-brand-primary);\n}\n\n.mj-forms-panel--related .mj-forms-panel-icon {\n color: var(--mj-brand-primary);\n}\n\n/* Related entity content \u2014 400px default, user can drag-resize vertically */\n.mj-forms-panel--related .mj-forms-panel-content {\n height: 400px;\n padding: 0;\n overflow: hidden;\n resize: vertical;\n min-height: 120px;\n max-height: 800px;\n}\n\n/* Ensure child chain fills the resizable content area */\n.mj-forms-panel--related .mj-forms-panel-content > *,\n.mj-forms-panel--related .mj-forms-panel-content > .ng-star-inserted {\n height: 100%;\n}\n\n.mj-forms-panel--related .mj-forms-panel-content > * > * {\n height: 100%;\n}\n\n/* Related entity panels need the body to not clip the resize handle */\n.mj-forms-panel--related .mj-forms-panel-body {\n overflow: visible;\n}\n\n/* ============================================\n BODY (collapsible)\n ============================================ */\n.mj-forms-panel-body {\n max-height: 2000px;\n overflow: hidden;\n transition: max-height 0.4s ease, padding 0.4s ease, opacity 0.3s ease;\n opacity: 1;\n}\n\n.mj-forms-panel-body--collapsed {\n max-height: 0;\n padding: 0;\n opacity: 0;\n}\n\n.mj-forms-panel-content {\n padding: 20px;\n overflow: auto;\n max-height: 600px;\n}\n\n/* ============================================\n SEARCH HIGHLIGHTING\n ============================================ */\n.mj-forms-panel-title ::ng-deep .mj-forms-search-highlight {\n /* Muted, theme-aware highlight (semantic token) \u2014 consistent with the field\n label + FK dropdown highlights. Replaces the old bright-yellow primitive. */\n background-color: var(--mj-search-highlight-bg);\n color: var(--mj-search-highlight-text);\n border-radius: 2px;\n padding: 0 1px;\n font-weight: 600;\n}\n"] }]
|
|
437
492
|
}], null, { SectionKey: [{
|
|
438
493
|
type: Input
|
|
439
494
|
}], SectionName: [{
|
|
@@ -454,6 +509,8 @@ export class MjCollapsiblePanelComponent {
|
|
|
454
509
|
type: Input
|
|
455
510
|
}], InheritedRecordPrimaryKey: [{
|
|
456
511
|
type: Input
|
|
512
|
+
}], Hidden: [{
|
|
513
|
+
type: Input
|
|
457
514
|
}], _deprecatedSectionKey: [{
|
|
458
515
|
type: Input,
|
|
459
516
|
args: ['sectionKey']
|
|
@@ -499,5 +556,5 @@ export class MjCollapsiblePanelComponent {
|
|
|
499
556
|
type: HostListener,
|
|
500
557
|
args: ['drop', ['$event']]
|
|
501
558
|
}] }); })();
|
|
502
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MjCollapsiblePanelComponent, { className: "MjCollapsiblePanelComponent", filePath: "src/lib/panel/collapsible-panel.component.ts", lineNumber:
|
|
559
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MjCollapsiblePanelComponent, { className: "MjCollapsiblePanelComponent", filePath: "src/lib/panel/collapsible-panel.component.ts", lineNumber: 55 }); })();
|
|
503
560
|
//# sourceMappingURL=collapsible-panel.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collapsible-panel.component.js","sourceRoot":"","sources":["../../../src/lib/panel/collapsible-panel.component.ts","../../../src/lib/panel/collapsible-panel.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component, Input, Output, EventEmitter,\n ChangeDetectionStrategy, ChangeDetectorRef, inject,\n OnChanges, SimpleChanges, OnInit, AfterContentInit, AfterViewInit, OnDestroy,\n ContentChildren, QueryList, HostBinding, HostListener, ElementRef,\n ViewChild, NgZone, ViewEncapsulation\n} from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\nimport { FormContext, PanelVariant, PanelDragStartEvent, PanelDropEvent } from '../types/form-types';\nimport { FormNavigationEvent } from '../types/navigation-events';\nimport { MjFormFieldComponent } from '../field/form-field.component';\nimport { CompositeKey } from '@memberjunction/core';\nimport { EscapeHTML, HighlightSearchMatches } from '@memberjunction/global';\n\n/**\n * Reusable collapsible panel for form sections.\n *\n * Supports three visual variants:\n * - **default**: White card with accent border (standard field sections)\n * - **related-entity**: Blue-accented card with row count badge (related entity grids)\n * - **inherited**: Purple-accented card with \"Inherited from X\" badge (IS-A parent field sections)\n *\n * Features:\n * - Expand/collapse with smooth animation\n * - Search filtering (hides non-matching panels, highlights matched names)\n * - Drag-to-reorder sections\n * - Inheritable \"Inherited from X\" badge with navigation event\n * - Row count badge for related entity sections\n *\n * @example\n * ```html\n * <mj-collapsible-panel\n * SectionKey=\"productDetails\"\n * SectionName=\"Product Details\"\n * Icon=\"fa fa-box\"\n * Variant=\"inherited\"\n * InheritedFromEntity=\"Products\"\n * [Form]=\"formComponent\"\n * [FormContext]=\"formContext\"\n * (Navigate)=\"onNavigate($event)\">\n * <mj-form-field ...></mj-form-field>\n * </mj-collapsible-panel>\n * ```\n */\n@Component({\n standalone: false,\n selector: 'mj-collapsible-panel',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None, // Required to style projected content (grids, etc.)\n templateUrl: './collapsible-panel.component.html',\n styleUrls: ['./collapsible-panel.component.css']\n})\nexport class MjCollapsiblePanelComponent implements OnInit, OnChanges, AfterContentInit, AfterViewInit, OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n private elementRef = inject(ElementRef);\n private ngZone = inject(NgZone);\n\n /** Unique key for state persistence */\n @Input() SectionKey = '';\n\n /** Display name shown in the panel header */\n @Input() SectionName = '';\n\n /** Font Awesome icon class for the panel header */\n @Input() Icon = 'fa fa-folder';\n\n /**\n * Reference to the parent form component for state delegation.\n * Expected to have IsSectionExpanded, SetSectionExpanded, getSectionDisplayOrder methods.\n */\n @Input() Form: unknown;\n\n /** Form-level context (search filter, showEmptyFields) */\n @Input() FormContext?: FormContext;\n\n /** Panel visual variant */\n @Input() Variant: PanelVariant = 'default';\n\n /** Row count badge for related entity sections */\n @Input() BadgeCount: number | undefined;\n\n /** Default expanded state when no persisted state exists */\n @Input() DefaultExpanded: boolean | undefined;\n\n /**\n * For 'inherited' variant: the parent entity name this section's fields come from.\n * Displayed as \"Inherited from X\" badge and used for navigation.\n */\n @Input() InheritedFromEntity = '';\n\n /**\n * For 'inherited' variant: the primary key to navigate to when clicking the badge.\n * (Shared PK in IS-A relationships.)\n */\n @Input() InheritedRecordPrimaryKey?: CompositeKey;\n\n // ---- Deprecated camelCase aliases (backward compat) ----\n\n /** @deprecated Use [SectionKey] instead */\n @Input('sectionKey') set _deprecatedSectionKey(value: string) { this.SectionKey = value; }\n\n /** @deprecated Use [SectionName] instead */\n @Input('sectionName') set _deprecatedSectionName(value: string) { this.SectionName = value; }\n\n /** @deprecated Use [Icon] instead */\n @Input('icon') set _deprecatedIcon(value: string) { this.Icon = value; }\n\n /** @deprecated Use [Form] instead */\n @Input('form') set _deprecatedForm(value: unknown) { this.Form = value; }\n\n /** @deprecated Use [FormContext] instead */\n @Input('formContext') set _deprecatedFormContext(value: FormContext | undefined) { this.FormContext = value; }\n\n // ---- Outputs ----\n\n @Output() DragStarted = new EventEmitter<PanelDragStartEvent>();\n @Output() DragEnded = new EventEmitter<void>();\n @Output() PanelDrop = new EventEmitter<PanelDropEvent>();\n @Output() Navigate = new EventEmitter<FormNavigationEvent>();\n\n // ---- Content Children ----\n\n @ContentChildren(MjFormFieldComponent, { descendants: true }) FieldComponents!: QueryList<MjFormFieldComponent>;\n\n // ---- State ----\n\n DisplayName = '';\n FieldNames = '';\n IsVisible = true;\n\n @HostBinding('class')\n get HostClass(): string {\n const classes = [`mj-panel--${this.Variant}`];\n if (!this.IsVisible) classes.push('mj-search-hidden');\n if (this.IsDragging) classes.push('mj-dragging');\n if (this.IsDragOver) classes.push('mj-drag-over');\n return classes.join(' ');\n }\n\n @HostBinding('style.order')\n get CssOrder(): number {\n const formRef = this.Form as { getSectionDisplayOrder?: (key: string) => number };\n return formRef?.getSectionDisplayOrder ? formRef.getSectionDisplayOrder(this.SectionKey) : 0;\n }\n\n IsDragging = false;\n IsDragOver = false;\n\n // ---- Event relay ----\n\n /** Signals re-subscription when ContentChildren change */\n private fieldNavReset$ = new Subject<void>();\n\n // ---- Panel content resize ----\n\n @ViewChild('panelContent') private panelContentRef?: ElementRef<HTMLElement>;\n private resizeObserver?: ResizeObserver;\n private resizeDebounceTimer?: ReturnType<typeof setTimeout>;\n /** ResizeObserver fires once synchronously on observe(); that initial measurement is not a user resize. */\n private resizeObserverPrimed = false;\n\n /**\n * Persisted panel height for related-entity panels.\n * Returns undefined to let CSS default (400px) apply.\n */\n get PanelContentHeight(): number | undefined {\n if (this.Variant !== 'related-entity') return undefined;\n const formRef = this.Form as { GetSectionPanelHeight?: (key: string) => number | undefined };\n return formRef?.GetSectionPanelHeight?.(this.SectionKey);\n }\n\n /** Whether drag-to-reorder is allowed (from FormContext) */\n get ReorderAllowed(): boolean {\n return this.FormContext?.allowSectionReorder !== false;\n }\n\n /** Whether the panel is expanded (delegates to form state) */\n get Expanded(): boolean {\n const formRef = this.Form as { IsSectionExpanded?: (key: string, defaultExpanded?: boolean) => boolean };\n return formRef?.IsSectionExpanded ? formRef.IsSectionExpanded(this.SectionKey, this.DefaultExpanded) : true;\n }\n\n // ---- Lifecycle ----\n\n ngOnInit(): void {\n this.DisplayName = this.SectionName;\n }\n\n ngAfterContentInit(): void {\n this.UpdateFieldNames();\n this.SubscribeToFieldNavigateEvents();\n this.FieldComponents.changes.subscribe(() => {\n this.UpdateFieldNames();\n this.SubscribeToFieldNavigateEvents();\n });\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['SectionName']) {\n this.DisplayName = this.SectionName;\n }\n if (changes['SectionName'] || changes['FormContext']) {\n this.UpdateVisibilityAndHighlighting();\n }\n if (changes['FormContext'] && this.FieldComponents) {\n this.FieldComponents.forEach(field => {\n field.FormContext = this.FormContext;\n });\n }\n }\n\n ngAfterViewInit(): void {\n this.SetupResizeObserver();\n }\n\n ngOnDestroy(): void {\n this.fieldNavReset$.next();\n this.fieldNavReset$.complete();\n this.resizeObserver?.disconnect();\n if (this.resizeDebounceTimer) {\n clearTimeout(this.resizeDebounceTimer);\n }\n }\n\n // ---- Actions ----\n\n Toggle(): void {\n const formRef = this.Form as { SetSectionExpanded?: (key: string, expanded: boolean) => void };\n if (formRef?.SetSectionExpanded) {\n formRef.SetSectionExpanded(this.SectionKey, !this.Expanded);\n this.cdr.markForCheck();\n }\n }\n\n /**\n * Navigate to the parent entity when clicking the \"Inherited from X\" badge.\n * Derives the PrimaryKey from the Form's record when InheritedRecordPrimaryKey\n * is not explicitly provided (which is the common case in generated templates).\n */\n OnInheritedBadgeClick(event: MouseEvent): void {\n event.stopPropagation();\n if (!this.InheritedFromEntity) return;\n\n const primaryKey = this.InheritedRecordPrimaryKey\n ?? (this.Form as { record?: { PrimaryKey: CompositeKey } })?.record?.PrimaryKey\n ?? new CompositeKey([]);\n\n this.Navigate.emit({\n Kind: 'entity-hierarchy',\n EntityName: this.InheritedFromEntity,\n PrimaryKey: primaryKey,\n Direction: 'parent'\n });\n }\n\n // ---- Drag and Drop ----\n\n @HostListener('dragover', ['$event'])\n OnDragOver(event: DragEvent): void {\n if (!this.ReorderAllowed) return;\n event.preventDefault();\n event.stopPropagation();\n if (event.dataTransfer?.types.includes('text/plain')) {\n this.IsDragOver = true;\n }\n }\n\n @HostListener('dragleave', ['$event'])\n OnDragLeave(event: DragEvent): void {\n if (!this.ReorderAllowed) return;\n event.preventDefault();\n this.IsDragOver = false;\n }\n\n @HostListener('drop', ['$event'])\n OnDrop(event: DragEvent): void {\n if (!this.ReorderAllowed) return;\n event.preventDefault();\n event.stopPropagation();\n this.IsDragOver = false;\n\n const sourceSectionKey = event.dataTransfer?.getData('text/plain');\n if (sourceSectionKey && sourceSectionKey !== this.SectionKey) {\n this.PanelDrop.emit({\n SourceSectionKey: sourceSectionKey,\n TargetSectionKey: this.SectionKey\n });\n this.ReorderSections(sourceSectionKey, this.SectionKey);\n }\n }\n\n OnDragStart(event: DragEvent): void {\n if (!this.ReorderAllowed) return;\n this.IsDragging = true;\n event.dataTransfer?.setData('text/plain', this.SectionKey);\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n }\n this.DragStarted.emit({ SectionKey: this.SectionKey, Event: event });\n }\n\n OnDragEnd(): void {\n this.IsDragging = false;\n this.DragEnded.emit();\n }\n\n // ---- Private Methods ----\n\n private ReorderSections(sourceSectionKey: string, targetSectionKey: string): void {\n const formRef = this.Form as {\n getSectionOrder?: () => string[];\n setSectionOrder?: (order: string[]) => void;\n };\n if (!formRef?.getSectionOrder || !formRef?.setSectionOrder) return;\n\n const currentOrder = formRef.getSectionOrder();\n const sourceIndex = currentOrder.indexOf(sourceSectionKey);\n const targetIndex = currentOrder.indexOf(targetSectionKey);\n if (sourceIndex === -1 || targetIndex === -1) return;\n\n const newOrder = [...currentOrder];\n newOrder.splice(sourceIndex, 1);\n newOrder.splice(targetIndex, 0, sourceSectionKey);\n formRef.setSectionOrder(newOrder);\n this.cdr.markForCheck();\n }\n\n private UpdateFieldNames(): void {\n if (this.FieldComponents) {\n const names: string[] = [];\n this.FieldComponents.forEach(field => {\n if (field.DisplayName) {\n names.push(field.DisplayName.toLowerCase());\n }\n });\n this.FieldNames = names.join(' ');\n this.UpdateVisibilityAndHighlighting();\n }\n }\n\n /**\n * Subscribes to Navigate events from all child form-field components\n * and relays them through this panel's Navigate output.\n */\n private SubscribeToFieldNavigateEvents(): void {\n this.fieldNavReset$.next(); // tear down previous subscriptions\n this.FieldComponents.forEach(field => {\n field.Navigate.pipe(takeUntil(this.fieldNavReset$)).subscribe((event: FormNavigationEvent) => {\n this.Navigate.emit(event);\n });\n });\n }\n\n /**\n * Sets up a ResizeObserver on the panel content div for related-entity panels.\n * When the user drags the CSS resize handle, we persist the new height.\n */\n private SetupResizeObserver(): void {\n if (this.Variant !== 'related-entity' || !this.panelContentRef) return;\n\n const el = this.panelContentRef.nativeElement;\n // Run outside Angular zone to avoid triggering change detection on every resize frame\n this.ngZone.runOutsideAngular(() => {\n this.resizeObserver = new ResizeObserver((entries) => {\n // Skip the synchronous initial fire emitted when observe() is called — it's the\n // panel's own first measurement on load, not a user-initiated resize. Persisting\n // it would write a spurious panelHeight for every panel on form open.\n if (!this.resizeObserverPrimed) {\n this.resizeObserverPrimed = true;\n return;\n }\n // Only a height change while the panel is expanded reflects a genuine user drag of\n // the resize handle. Ignore reflows while collapsed (e.g. content settling on load).\n if (!this.Expanded) return;\n const entry = entries[0];\n if (!entry) return;\n const newHeight = Math.round(entry.contentRect.height);\n this.DebouncePersistHeight(newHeight);\n });\n this.resizeObserver.observe(el);\n });\n }\n\n /**\n * Debounces height persistence so we don't write to DB on every resize frame.\n */\n private DebouncePersistHeight(height: number): void {\n if (this.resizeDebounceTimer) {\n clearTimeout(this.resizeDebounceTimer);\n }\n this.resizeDebounceTimer = setTimeout(() => {\n const formRef = this.Form as { SetSectionPanelHeight?: (key: string, height: number) => void };\n formRef?.SetSectionPanelHeight?.(this.SectionKey, height);\n }, 500);\n }\n\n private UpdateVisibilityAndHighlighting(): void {\n const searchTerm = (this.FormContext?.sectionFilter || '').toLowerCase().trim();\n\n if (!searchTerm) {\n this.IsVisible = true;\n this.DisplayName = this.SectionName;\n this.cdr.markForCheck();\n return;\n }\n\n const sectionMatches = this.SectionName.toLowerCase().includes(searchTerm);\n const fieldsMatch = this.FieldNames.includes(searchTerm);\n this.IsVisible = sectionMatches || fieldsMatch;\n\n // DisplayName is bound to `[innerHTML]` in the template — must always be HTML-safe.\n this.DisplayName =\n this.IsVisible && sectionMatches\n ? HighlightSearchMatches(this.SectionName, searchTerm, 'mj-forms-search-highlight')\n : EscapeHTML(this.SectionName);\n\n this.cdr.markForCheck();\n }\n}\n","<div class=\"mj-forms-panel\"\n [class.mj-forms-panel--related]=\"Variant === 'related-entity'\"\n [class.mj-forms-panel--inherited]=\"Variant === 'inherited'\"\n [attr.data-section-key]=\"SectionKey\">\n\n <!-- Panel Header -->\n <div class=\"mj-forms-panel-header\" role=\"button\" tabindex=\"0\" (click)=\"Toggle()\">\n <!-- Drag Handle (hidden when reorder disabled) -->\n @if (ReorderAllowed) {\n <div class=\"mj-forms-drag-handle\"\n title=\"Drag to reorder sections\"\n draggable=\"true\"\n (dragstart)=\"OnDragStart($event)\"\n (dragend)=\"OnDragEnd()\"\n (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-grip-vertical\"></i>\n </div>\n }\n\n <!-- Icon + Title -->\n <div class=\"mj-forms-panel-title-group\">\n <i [class]=\"Icon\" class=\"mj-forms-panel-icon\"></i>\n <h3 class=\"mj-forms-panel-title\">\n <span [innerHTML]=\"DisplayName\"></span>\n <!-- Row count badge for related entities -->\n @if (BadgeCount !== undefined) {\n <span class=\"mj-forms-row-count-badge\" [class.mj-forms-row-count-badge--zero]=\"BadgeCount === 0\">\n {{ BadgeCount }}\n </span>\n }\n </h3>\n </div>\n\n <!-- Inherited From badge (IS-A) -->\n @if (Variant === 'inherited' && InheritedFromEntity) {\n <a class=\"mj-forms-inherited-badge\" (click)=\"OnInheritedBadgeClick($event)\"\n [title]=\"'View parent record: ' + InheritedFromEntity\">\n <i class=\"fa-solid fa-arrow-up\"></i>\n Inherited from {{ InheritedFromEntity }}\n </a>\n }\n\n <!-- Chevron -->\n <div class=\"mj-forms-panel-chevron\">\n <i [class]=\"Expanded ? 'fa fa-chevron-up' : 'fa fa-chevron-down'\"></i>\n </div>\n </div>\n\n <!-- Panel Body -->\n <div class=\"mj-forms-panel-body\" [class.mj-forms-panel-body--collapsed]=\"!Expanded\">\n <div class=\"mj-forms-panel-content\"\n #panelContent\n [style.height.px]=\"PanelContentHeight\">\n <ng-content></ng-content>\n </div>\n </div>\n</div>\n"]}
|
|
1
|
+
{"version":3,"file":"collapsible-panel.component.js","sourceRoot":"","sources":["../../../src/lib/panel/collapsible-panel.component.ts","../../../src/lib/panel/collapsible-panel.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component, Input, Output, EventEmitter,\n ChangeDetectionStrategy, ChangeDetectorRef, inject,\n OnChanges, SimpleChanges, OnInit, AfterContentInit, AfterViewInit, OnDestroy,\n ContentChildren, QueryList, HostBinding, HostListener, ElementRef,\n ViewChild, NgZone, ViewEncapsulation\n} from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\nimport { FormContext, PanelVariant, PanelDragStartEvent, PanelDropEvent } from '../types/form-types';\nimport { IsFormSectionHidden } from '../types/entity-form-config';\nimport { FormNavigationEvent } from '../types/navigation-events';\nimport { MjFormFieldComponent } from '../field/form-field.component';\nimport { CompositeKey } from '@memberjunction/core';\nimport { EscapeHTML, HighlightSearchMatches } from '@memberjunction/global';\n\n/**\n * Reusable collapsible panel for form sections.\n *\n * Supports three visual variants:\n * - **default**: White card with accent border (standard field sections)\n * - **related-entity**: Blue-accented card with row count badge (related entity grids)\n * - **inherited**: Purple-accented card with \"Inherited from X\" badge (IS-A parent field sections)\n *\n * Features:\n * - Expand/collapse with smooth animation\n * - Search filtering (hides non-matching panels, highlights matched names)\n * - Drag-to-reorder sections\n * - Inheritable \"Inherited from X\" badge with navigation event\n * - Row count badge for related entity sections\n *\n * @example\n * ```html\n * <mj-collapsible-panel\n * SectionKey=\"productDetails\"\n * SectionName=\"Product Details\"\n * Icon=\"fa fa-box\"\n * Variant=\"inherited\"\n * InheritedFromEntity=\"Products\"\n * [Form]=\"formComponent\"\n * [FormContext]=\"formContext\"\n * (Navigate)=\"onNavigate($event)\">\n * <mj-form-field ...></mj-form-field>\n * </mj-collapsible-panel>\n * ```\n */\n@Component({\n standalone: false,\n selector: 'mj-collapsible-panel',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None, // Required to style projected content (grids, etc.)\n templateUrl: './collapsible-panel.component.html',\n styleUrls: ['./collapsible-panel.component.css']\n})\nexport class MjCollapsiblePanelComponent implements OnInit, OnChanges, AfterContentInit, AfterViewInit, OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n private elementRef = inject(ElementRef);\n private ngZone = inject(NgZone);\n\n /** Unique key for state persistence */\n @Input() SectionKey = '';\n\n /** Display name shown in the panel header */\n @Input() SectionName = '';\n\n /** Font Awesome icon class for the panel header */\n @Input() Icon = 'fa fa-folder';\n\n /**\n * Reference to the parent form component for state delegation.\n * Expected to have IsSectionExpanded, SetSectionExpanded, getSectionDisplayOrder methods.\n */\n @Input() Form: unknown;\n\n /** Form-level context (search filter, showEmptyFields) */\n @Input() FormContext?: FormContext;\n\n /** Panel visual variant */\n @Input() Variant: PanelVariant = 'default';\n\n /** Row count badge for related entity sections */\n @Input() BadgeCount: number | undefined;\n\n /** Default expanded state when no persisted state exists */\n @Input() DefaultExpanded: boolean | undefined;\n\n /**\n * For 'inherited' variant: the parent entity name this section's fields come from.\n * Displayed as \"Inherited from X\" badge and used for navigation.\n */\n @Input() InheritedFromEntity = '';\n\n /**\n * For 'inherited' variant: the primary key to navigate to when clicking the badge.\n * (Shared PK in IS-A relationships.)\n */\n @Input() InheritedRecordPrimaryKey?: CompositeKey;\n\n /**\n * Container-driven hard hide. When true the panel is removed from view\n * regardless of search state — used by the record-form container to honor\n * `EntityFormConfig.showRelatedEntities` / `hiddenSectionKeys` /\n * `visibleSectionKeys`. Composes with (overrides) search visibility so all\n * existing `IsVisible`-based section counts stay correct.\n */\n @Input()\n set Hidden(value: boolean) {\n if (value !== this._hidden) {\n this._hidden = value;\n // Recompute visibility if content has initialized (FieldComponents present).\n if (this.FieldComponents) {\n this.UpdateVisibilityAndHighlighting();\n }\n }\n }\n get Hidden(): boolean {\n return this._hidden;\n }\n private _hidden = false;\n\n // ---- Deprecated camelCase aliases (backward compat) ----\n\n /** @deprecated Use [SectionKey] instead */\n @Input('sectionKey') set _deprecatedSectionKey(value: string) { this.SectionKey = value; }\n\n /** @deprecated Use [SectionName] instead */\n @Input('sectionName') set _deprecatedSectionName(value: string) { this.SectionName = value; }\n\n /** @deprecated Use [Icon] instead */\n @Input('icon') set _deprecatedIcon(value: string) { this.Icon = value; }\n\n /** @deprecated Use [Form] instead */\n @Input('form') set _deprecatedForm(value: unknown) { this.Form = value; }\n\n /** @deprecated Use [FormContext] instead */\n @Input('formContext') set _deprecatedFormContext(value: FormContext | undefined) { this.FormContext = value; }\n\n // ---- Outputs ----\n\n @Output() DragStarted = new EventEmitter<PanelDragStartEvent>();\n @Output() DragEnded = new EventEmitter<void>();\n @Output() PanelDrop = new EventEmitter<PanelDropEvent>();\n @Output() Navigate = new EventEmitter<FormNavigationEvent>();\n\n // ---- Content Children ----\n\n @ContentChildren(MjFormFieldComponent, { descendants: true }) FieldComponents!: QueryList<MjFormFieldComponent>;\n\n // ---- State ----\n\n DisplayName = '';\n FieldNames = '';\n IsVisible = true;\n\n @HostBinding('class')\n get HostClass(): string {\n const classes = [`mj-panel--${this.Variant}`];\n if (!this.IsVisible) classes.push('mj-search-hidden');\n if (this.IsDragging) classes.push('mj-dragging');\n if (this.IsDragOver) classes.push('mj-drag-over');\n return classes.join(' ');\n }\n\n @HostBinding('style.order')\n get CssOrder(): number {\n const formRef = this.Form as { getSectionDisplayOrder?: (key: string) => number };\n return formRef?.getSectionDisplayOrder ? formRef.getSectionDisplayOrder(this.SectionKey) : 0;\n }\n\n IsDragging = false;\n IsDragOver = false;\n\n // ---- Event relay ----\n\n /** Signals re-subscription when ContentChildren change */\n private fieldNavReset$ = new Subject<void>();\n\n // ---- Panel content resize ----\n\n @ViewChild('panelContent') private panelContentRef?: ElementRef<HTMLElement>;\n private resizeObserver?: ResizeObserver;\n private resizeDebounceTimer?: ReturnType<typeof setTimeout>;\n /** ResizeObserver fires once synchronously on observe(); that initial measurement is not a user resize. */\n private resizeObserverPrimed = false;\n\n /**\n * Persisted panel height for related-entity panels.\n * Returns undefined to let CSS default (400px) apply.\n */\n get PanelContentHeight(): number | undefined {\n if (this.Variant !== 'related-entity') return undefined;\n const formRef = this.Form as { GetSectionPanelHeight?: (key: string) => number | undefined };\n return formRef?.GetSectionPanelHeight?.(this.SectionKey);\n }\n\n /** Whether drag-to-reorder is allowed (from FormContext) */\n get ReorderAllowed(): boolean {\n return this.FormContext?.allowSectionReorder !== false;\n }\n\n /**\n * Whether this panel's header may collapse/expand. Driven by\n * `FormContext.collapsibleSections`; when false the panel renders\n * always-expanded with no toggle chevron (used by dialog/slide-in surfaces\n * that lock sections open). Undefined / true means collapsible.\n */\n get Collapsible(): boolean {\n return this.FormContext?.collapsibleSections !== false;\n }\n\n /** Whether the panel is expanded (delegates to form state; always true when not collapsible) */\n get Expanded(): boolean {\n if (!this.Collapsible) return true;\n const formRef = this.Form as { IsSectionExpanded?: (key: string, defaultExpanded?: boolean) => boolean };\n return formRef?.IsSectionExpanded ? formRef.IsSectionExpanded(this.SectionKey, this.DefaultExpanded) : true;\n }\n\n // ---- Lifecycle ----\n\n ngOnInit(): void {\n this.DisplayName = this.SectionName;\n }\n\n ngAfterContentInit(): void {\n this.UpdateFieldNames();\n this.SubscribeToFieldNavigateEvents();\n this.FieldComponents.changes.subscribe(() => {\n this.UpdateFieldNames();\n this.SubscribeToFieldNavigateEvents();\n });\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['SectionName']) {\n this.DisplayName = this.SectionName;\n }\n if (changes['SectionName'] || changes['FormContext']) {\n this.UpdateVisibilityAndHighlighting();\n }\n if (changes['FormContext'] && this.FieldComponents) {\n this.FieldComponents.forEach(field => {\n field.FormContext = this.FormContext;\n });\n }\n }\n\n ngAfterViewInit(): void {\n this.SetupResizeObserver();\n }\n\n ngOnDestroy(): void {\n this.fieldNavReset$.next();\n this.fieldNavReset$.complete();\n this.resizeObserver?.disconnect();\n if (this.resizeDebounceTimer) {\n clearTimeout(this.resizeDebounceTimer);\n }\n }\n\n // ---- Actions ----\n\n Toggle(): void {\n if (!this.Collapsible) return;\n const formRef = this.Form as { SetSectionExpanded?: (key: string, expanded: boolean) => void };\n if (formRef?.SetSectionExpanded) {\n formRef.SetSectionExpanded(this.SectionKey, !this.Expanded);\n this.cdr.markForCheck();\n }\n }\n\n /**\n * Navigate to the parent entity when clicking the \"Inherited from X\" badge.\n * Derives the PrimaryKey from the Form's record when InheritedRecordPrimaryKey\n * is not explicitly provided (which is the common case in generated templates).\n */\n OnInheritedBadgeClick(event: MouseEvent): void {\n event.stopPropagation();\n if (!this.InheritedFromEntity) return;\n\n const primaryKey = this.InheritedRecordPrimaryKey\n ?? (this.Form as { record?: { PrimaryKey: CompositeKey } })?.record?.PrimaryKey\n ?? new CompositeKey([]);\n\n this.Navigate.emit({\n Kind: 'entity-hierarchy',\n EntityName: this.InheritedFromEntity,\n PrimaryKey: primaryKey,\n Direction: 'parent'\n });\n }\n\n // ---- Drag and Drop ----\n\n @HostListener('dragover', ['$event'])\n OnDragOver(event: DragEvent): void {\n if (!this.ReorderAllowed) return;\n event.preventDefault();\n event.stopPropagation();\n if (event.dataTransfer?.types.includes('text/plain')) {\n this.IsDragOver = true;\n }\n }\n\n @HostListener('dragleave', ['$event'])\n OnDragLeave(event: DragEvent): void {\n if (!this.ReorderAllowed) return;\n event.preventDefault();\n this.IsDragOver = false;\n }\n\n @HostListener('drop', ['$event'])\n OnDrop(event: DragEvent): void {\n if (!this.ReorderAllowed) return;\n event.preventDefault();\n event.stopPropagation();\n this.IsDragOver = false;\n\n const sourceSectionKey = event.dataTransfer?.getData('text/plain');\n if (sourceSectionKey && sourceSectionKey !== this.SectionKey) {\n this.PanelDrop.emit({\n SourceSectionKey: sourceSectionKey,\n TargetSectionKey: this.SectionKey\n });\n this.ReorderSections(sourceSectionKey, this.SectionKey);\n }\n }\n\n OnDragStart(event: DragEvent): void {\n if (!this.ReorderAllowed) return;\n this.IsDragging = true;\n event.dataTransfer?.setData('text/plain', this.SectionKey);\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n }\n this.DragStarted.emit({ SectionKey: this.SectionKey, Event: event });\n }\n\n OnDragEnd(): void {\n this.IsDragging = false;\n this.DragEnded.emit();\n }\n\n // ---- Private Methods ----\n\n private ReorderSections(sourceSectionKey: string, targetSectionKey: string): void {\n const formRef = this.Form as {\n getSectionOrder?: () => string[];\n setSectionOrder?: (order: string[]) => void;\n };\n if (!formRef?.getSectionOrder || !formRef?.setSectionOrder) return;\n\n const currentOrder = formRef.getSectionOrder();\n const sourceIndex = currentOrder.indexOf(sourceSectionKey);\n const targetIndex = currentOrder.indexOf(targetSectionKey);\n if (sourceIndex === -1 || targetIndex === -1) return;\n\n const newOrder = [...currentOrder];\n newOrder.splice(sourceIndex, 1);\n newOrder.splice(targetIndex, 0, sourceSectionKey);\n formRef.setSectionOrder(newOrder);\n this.cdr.markForCheck();\n }\n\n private UpdateFieldNames(): void {\n if (this.FieldComponents) {\n const names: string[] = [];\n this.FieldComponents.forEach(field => {\n if (field.DisplayName) {\n names.push(field.DisplayName.toLowerCase());\n }\n });\n this.FieldNames = names.join(' ');\n this.UpdateVisibilityAndHighlighting();\n }\n }\n\n /**\n * Subscribes to Navigate events from all child form-field components\n * and relays them through this panel's Navigate output.\n */\n private SubscribeToFieldNavigateEvents(): void {\n this.fieldNavReset$.next(); // tear down previous subscriptions\n this.FieldComponents.forEach(field => {\n field.Navigate.pipe(takeUntil(this.fieldNavReset$)).subscribe((event: FormNavigationEvent) => {\n this.Navigate.emit(event);\n });\n });\n }\n\n /**\n * Sets up a ResizeObserver on the panel content div for related-entity panels.\n * When the user drags the CSS resize handle, we persist the new height.\n */\n private SetupResizeObserver(): void {\n if (this.Variant !== 'related-entity' || !this.panelContentRef) return;\n\n const el = this.panelContentRef.nativeElement;\n // Run outside Angular zone to avoid triggering change detection on every resize frame\n this.ngZone.runOutsideAngular(() => {\n this.resizeObserver = new ResizeObserver((entries) => {\n // Skip the synchronous initial fire emitted when observe() is called — it's the\n // panel's own first measurement on load, not a user-initiated resize. Persisting\n // it would write a spurious panelHeight for every panel on form open.\n if (!this.resizeObserverPrimed) {\n this.resizeObserverPrimed = true;\n return;\n }\n // Only a height change while the panel is expanded reflects a genuine user drag of\n // the resize handle. Ignore reflows while collapsed (e.g. content settling on load).\n if (!this.Expanded) return;\n const entry = entries[0];\n if (!entry) return;\n const newHeight = Math.round(entry.contentRect.height);\n this.DebouncePersistHeight(newHeight);\n });\n this.resizeObserver.observe(el);\n });\n }\n\n /**\n * Debounces height persistence so we don't write to DB on every resize frame.\n */\n private DebouncePersistHeight(height: number): void {\n if (this.resizeDebounceTimer) {\n clearTimeout(this.resizeDebounceTimer);\n }\n this.resizeDebounceTimer = setTimeout(() => {\n const formRef = this.Form as { SetSectionPanelHeight?: (key: string, height: number) => void };\n formRef?.SetSectionPanelHeight?.(this.SectionKey, height);\n }, 500);\n }\n\n private UpdateVisibilityAndHighlighting(): void {\n // Hard hide takes precedence over search state. Driven by an explicit\n // `Hidden` input OR the form config's section-visibility rules carried on\n // FormContext (which also reach slot-injected BaseFormPanels, since every\n // panel receives FormContext).\n if (this._hidden || IsFormSectionHidden(this.FormContext, this.SectionKey, this.Variant)) {\n this.IsVisible = false;\n this.DisplayName = EscapeHTML(this.SectionName);\n this.cdr.markForCheck();\n return;\n }\n\n const searchTerm = (this.FormContext?.sectionFilter || '').toLowerCase().trim();\n\n if (!searchTerm) {\n this.IsVisible = true;\n this.DisplayName = this.SectionName;\n this.cdr.markForCheck();\n return;\n }\n\n const sectionMatches = this.SectionName.toLowerCase().includes(searchTerm);\n const fieldsMatch = this.FieldNames.includes(searchTerm);\n this.IsVisible = sectionMatches || fieldsMatch;\n\n // DisplayName is bound to `[innerHTML]` in the template — must always be HTML-safe.\n this.DisplayName =\n this.IsVisible && sectionMatches\n ? HighlightSearchMatches(this.SectionName, searchTerm, 'mj-forms-search-highlight')\n : EscapeHTML(this.SectionName);\n\n this.cdr.markForCheck();\n }\n}\n","<div class=\"mj-forms-panel\"\n [class.mj-forms-panel--related]=\"Variant === 'related-entity'\"\n [class.mj-forms-panel--inherited]=\"Variant === 'inherited'\"\n [attr.data-section-key]=\"SectionKey\">\n\n <!-- Panel Header -->\n <div class=\"mj-forms-panel-header\"\n [class.mj-forms-panel-header--static]=\"!Collapsible\"\n [attr.role]=\"Collapsible ? 'button' : null\"\n [attr.tabindex]=\"Collapsible ? 0 : null\"\n (click)=\"Toggle()\">\n <!-- Drag Handle (hidden when reorder disabled) -->\n @if (ReorderAllowed) {\n <div class=\"mj-forms-drag-handle\"\n title=\"Drag to reorder sections\"\n draggable=\"true\"\n (dragstart)=\"OnDragStart($event)\"\n (dragend)=\"OnDragEnd()\"\n (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-grip-vertical\"></i>\n </div>\n }\n\n <!-- Icon + Title -->\n <div class=\"mj-forms-panel-title-group\">\n <i [class]=\"Icon\" class=\"mj-forms-panel-icon\"></i>\n <h3 class=\"mj-forms-panel-title\">\n <span [innerHTML]=\"DisplayName\"></span>\n <!-- Row count badge for related entities -->\n @if (BadgeCount !== undefined) {\n <span class=\"mj-forms-row-count-badge\" [class.mj-forms-row-count-badge--zero]=\"BadgeCount === 0\">\n {{ BadgeCount }}\n </span>\n }\n </h3>\n </div>\n\n <!-- Inherited From badge (IS-A) -->\n @if (Variant === 'inherited' && InheritedFromEntity) {\n <a class=\"mj-forms-inherited-badge\" (click)=\"OnInheritedBadgeClick($event)\"\n [title]=\"'View parent record: ' + InheritedFromEntity\">\n <i class=\"fa-solid fa-arrow-up\"></i>\n Inherited from {{ InheritedFromEntity }}\n </a>\n }\n\n <!-- Chevron (hidden when sections are locked open) -->\n @if (Collapsible) {\n <div class=\"mj-forms-panel-chevron\">\n <i [class]=\"Expanded ? 'fa fa-chevron-up' : 'fa fa-chevron-down'\"></i>\n </div>\n }\n </div>\n\n <!-- Panel Body -->\n <div class=\"mj-forms-panel-body\" [class.mj-forms-panel-body--collapsed]=\"!Expanded\">\n <div class=\"mj-forms-panel-content\"\n #panelContent\n [style.height.px]=\"PanelContentHeight\">\n <ng-content></ng-content>\n </div>\n </div>\n</div>\n"]}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { Type } from '@angular/core';
|
|
2
|
+
import { IMetadataProvider, UserInfo, EntityInfo } from '@memberjunction/core';
|
|
3
|
+
import { BaseFormComponent } from '../base-form-component';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
/**
|
|
6
|
+
* Slim row shape for an `EntityFormOverride` lookup. Resolution doesn't need
|
|
7
|
+
* the full BaseEntity object — `simple` ResultType is faster and avoids a
|
|
8
|
+
* compile-time dependency on the generated entity class. The row is only ever
|
|
9
|
+
* read by the resolver; mutation goes through the generated entity in other
|
|
10
|
+
* code paths (Studio, AI authoring).
|
|
11
|
+
*/
|
|
12
|
+
export interface EntityFormOverrideRow {
|
|
13
|
+
ID: string;
|
|
14
|
+
EntityID: string;
|
|
15
|
+
ComponentID: string;
|
|
16
|
+
Scope: 'User' | 'Role' | 'Global';
|
|
17
|
+
UserID: string | null;
|
|
18
|
+
RoleID: string | null;
|
|
19
|
+
Priority: number;
|
|
20
|
+
Status: 'Active' | 'Inactive' | 'Pending';
|
|
21
|
+
Name?: string;
|
|
22
|
+
Description?: string | null;
|
|
23
|
+
}
|
|
24
|
+
/** Resolved form kind for a given (entity, user, roles) tuple. */
|
|
25
|
+
export type FormResolution = {
|
|
26
|
+
kind: 'interactive';
|
|
27
|
+
override: EntityFormOverrideRow;
|
|
28
|
+
variants: EntityFormOverrideRow[];
|
|
29
|
+
} | {
|
|
30
|
+
kind: 'class';
|
|
31
|
+
subClass: Type<BaseFormComponent>;
|
|
32
|
+
variants: EntityFormOverrideRow[];
|
|
33
|
+
} | {
|
|
34
|
+
kind: 'none';
|
|
35
|
+
variants: EntityFormOverrideRow[];
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Picks the form to render for an entity record and exposes the full list of
|
|
39
|
+
* applicable variants so the toolbar's variant switcher can offer alternates.
|
|
40
|
+
*
|
|
41
|
+
* Tier order for the **default** pick:
|
|
42
|
+
* 1. `EntityFormOverride` row matching the caller's User/Role/Global scope,
|
|
43
|
+
* Status='Active', ordered by scope tier (User > Role > Global), then
|
|
44
|
+
* Priority DESC, then `__mj_CreatedAt DESC`. First row wins.
|
|
45
|
+
* 2. Existing `ClassFactory.GetRegistration(BaseFormComponent, entityName)` —
|
|
46
|
+
* the @RegisterClass + CodeGen-generated path used today.
|
|
47
|
+
* 3. None — caller surfaces the "no form registered" error.
|
|
48
|
+
*
|
|
49
|
+
* **Session selection.** If the user previously chose a non-default variant
|
|
50
|
+
* via the variant switcher and that choice is still applicable + Active, that
|
|
51
|
+
* choice wins over the default. Choice is keyed by entity name in
|
|
52
|
+
* `localStorage`. Honoring this is part of the variant-switcher contract;
|
|
53
|
+
* without it, switching variants would only last for the lifetime of the
|
|
54
|
+
* record-form view.
|
|
55
|
+
*
|
|
56
|
+
* **Performance.** Backed by `InteractiveFormsEngine` (in MJCoreEntities)
|
|
57
|
+
* — overrides are cached in memory with BaseEntity event-driven
|
|
58
|
+
* invalidation, so resolution is an O(N) JS filter against a small
|
|
59
|
+
* set instead of a per-LoadForm RunView round-trip. Cold latency
|
|
60
|
+
* dropped from ~50ms to <1ms after the first load. The engine is
|
|
61
|
+
* lazy-Config'd here on first resolution; users who never open a
|
|
62
|
+
* record pay nothing.
|
|
63
|
+
*/
|
|
64
|
+
export declare class FormResolverService {
|
|
65
|
+
ResolveFormForEntity(entity: EntityInfo, user: UserInfo, provider: IMetadataProvider): Promise<FormResolution>;
|
|
66
|
+
/**
|
|
67
|
+
* Public list-API for the variant switcher UI. Returns all variants
|
|
68
|
+
* applicable to (entity, user). Includes Active, Pending, and Inactive
|
|
69
|
+
* rows so the picker can offer "switch back to v1.0.0" alongside the
|
|
70
|
+
* current active variant.
|
|
71
|
+
*/
|
|
72
|
+
ListVariantsForEntity(entity: EntityInfo, user: UserInfo, provider: IMetadataProvider): Promise<EntityFormOverrideRow[]>;
|
|
73
|
+
/**
|
|
74
|
+
* Sentinel stored in localStorage when the user **explicitly picks the
|
|
75
|
+
* "Default form"** option from the variant picker. Distinct from a
|
|
76
|
+
* missing localStorage key (= "no preference, use auto-pick rules")
|
|
77
|
+
* and from an override UUID. Without this sentinel, picking "Default"
|
|
78
|
+
* cleared the preference and `pickActive` re-applied the auto-pick —
|
|
79
|
+
* which always selects the first Active override, making the
|
|
80
|
+
* CodeGen/Angular fallback unreachable from the UI.
|
|
81
|
+
*
|
|
82
|
+
* Format: a leading `__` makes it visually distinct from a UUID and
|
|
83
|
+
* impossible to collide with one (UUIDs don't contain underscores).
|
|
84
|
+
*/
|
|
85
|
+
static readonly EXPLICIT_DEFAULT_SENTINEL = "__codegen-default__";
|
|
86
|
+
/**
|
|
87
|
+
* Build the per-entity setting key. Lowercased so case variants of
|
|
88
|
+
* the entity name collapse onto a single record in `MJ: User Settings`.
|
|
89
|
+
*/
|
|
90
|
+
private settingKey;
|
|
91
|
+
/**
|
|
92
|
+
* Persist a specific override choice. Pass the override UUID. Writes
|
|
93
|
+
* via `UserInfoEngine.SetSettingDebounced` so rapid successive picks
|
|
94
|
+
* don't hammer the DB. Use {@link SetExplicitDefault} to record "user
|
|
95
|
+
* wants the CodeGen Angular fallback" and {@link ClearSelectedVariant}
|
|
96
|
+
* to wipe the preference entirely (revert to auto-pick).
|
|
97
|
+
*/
|
|
98
|
+
SetSelectedVariant(entityName: string, overrideID: string): void;
|
|
99
|
+
/**
|
|
100
|
+
* Record that the user explicitly picked the "Default form" row in
|
|
101
|
+
* the picker. `pickActive` reads this sentinel and returns null even
|
|
102
|
+
* when Active overrides exist for the entity, so the form-loading
|
|
103
|
+
* path falls back to CodeGen's `@RegisterClass` lookup.
|
|
104
|
+
*/
|
|
105
|
+
SetExplicitDefault(entityName: string): void;
|
|
106
|
+
/**
|
|
107
|
+
* Wipe the user's stored preference for this entity. Next load
|
|
108
|
+
* applies the auto-pick rules (first Active override in tier order).
|
|
109
|
+
* Called internally when a saved override ID is no longer valid.
|
|
110
|
+
* Fire-and-forget — the resolver doesn't need to await the delete.
|
|
111
|
+
*/
|
|
112
|
+
ClearSelectedVariant(entityName: string): void;
|
|
113
|
+
/**
|
|
114
|
+
* Read the user's previously-saved variant choice for the entity.
|
|
115
|
+
* Synchronous because `UserInfoEngine` keeps the user-settings table
|
|
116
|
+
* in memory after bootstrap. Returns the stored UUID, the explicit-
|
|
117
|
+
* default sentinel, or null when no preference exists.
|
|
118
|
+
*/
|
|
119
|
+
GetSelectedVariant(entityName: string): string | null;
|
|
120
|
+
private listApplicableVariants;
|
|
121
|
+
/**
|
|
122
|
+
* Pick the override that should actually render given the variant list
|
|
123
|
+
* and the user's stored selection (if any).
|
|
124
|
+
*
|
|
125
|
+
* Selection rules:
|
|
126
|
+
* - If the user explicitly picked the "Default form" row in the
|
|
127
|
+
* variant picker (sentinel in localStorage) → return null so the
|
|
128
|
+
* form-loading path falls back to CodeGen's `@RegisterClass` lookup.
|
|
129
|
+
* This is what makes the Angular fallback reachable from the UI.
|
|
130
|
+
* - Else if the user has a saved variant ID AND that variant is in
|
|
131
|
+
* the applicable list AND it's Active → use it.
|
|
132
|
+
* - Else → first Active row in tier+priority order (auto-pick).
|
|
133
|
+
* - Else → null (fall back to CodeGen/@RegisterClass path).
|
|
134
|
+
*/
|
|
135
|
+
private pickActive;
|
|
136
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FormResolverService, never>;
|
|
137
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<FormResolverService>;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=form-resolver.service.d.ts.map
|