@memberjunction/ng-base-forms 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/lib/base-form-component.d.ts +103 -215
  2. package/dist/lib/base-form-component.d.ts.map +1 -1
  3. package/dist/lib/base-form-component.js +384 -541
  4. package/dist/lib/base-form-component.js.map +1 -1
  5. package/dist/lib/container/record-form-container.component.d.ts +165 -0
  6. package/dist/lib/container/record-form-container.component.d.ts.map +1 -0
  7. package/dist/lib/container/record-form-container.component.js +534 -0
  8. package/dist/lib/container/record-form-container.component.js.map +1 -0
  9. package/dist/lib/explorer-entity-data-grid.component.d.ts +7 -5
  10. package/dist/lib/explorer-entity-data-grid.component.d.ts.map +1 -1
  11. package/dist/lib/explorer-entity-data-grid.component.js +15 -9
  12. package/dist/lib/explorer-entity-data-grid.component.js.map +1 -1
  13. package/dist/lib/field/form-field.component.d.ts +268 -0
  14. package/dist/lib/field/form-field.component.d.ts.map +1 -0
  15. package/dist/lib/field/form-field.component.js +1194 -0
  16. package/dist/lib/field/form-field.component.js.map +1 -0
  17. package/dist/lib/form-state.interface.d.ts +2 -0
  18. package/dist/lib/form-state.interface.d.ts.map +1 -1
  19. package/dist/lib/form-state.interface.js.map +1 -1
  20. package/dist/lib/form-state.service.d.ts +14 -0
  21. package/dist/lib/form-state.service.d.ts.map +1 -1
  22. package/dist/lib/form-state.service.js +19 -0
  23. package/dist/lib/form-state.service.js.map +1 -1
  24. package/dist/lib/panel/collapsible-panel.component.d.ts +139 -0
  25. package/dist/lib/panel/collapsible-panel.component.d.ts.map +1 -0
  26. package/dist/lib/panel/collapsible-panel.component.js +468 -0
  27. package/dist/lib/panel/collapsible-panel.component.js.map +1 -0
  28. package/dist/lib/toolbar/form-toolbar.component.d.ts +170 -0
  29. package/dist/lib/toolbar/form-toolbar.component.d.ts.map +1 -0
  30. package/dist/lib/toolbar/form-toolbar.component.js +830 -0
  31. package/dist/lib/toolbar/form-toolbar.component.js.map +1 -0
  32. package/dist/lib/types/form-events.d.ts +90 -0
  33. package/dist/lib/types/form-events.d.ts.map +1 -0
  34. package/dist/lib/types/form-events.js +45 -0
  35. package/dist/lib/types/form-events.js.map +1 -0
  36. package/dist/lib/types/form-types.d.ts +150 -0
  37. package/dist/lib/types/form-types.d.ts.map +1 -0
  38. package/dist/lib/types/form-types.js +12 -0
  39. package/dist/lib/types/form-types.js.map +1 -0
  40. package/dist/lib/types/navigation-events.d.ts +88 -0
  41. package/dist/lib/types/navigation-events.d.ts.map +1 -0
  42. package/dist/lib/types/navigation-events.js +2 -0
  43. package/dist/lib/types/navigation-events.js.map +1 -0
  44. package/dist/lib/types/toolbar-config.d.ts +66 -0
  45. package/dist/lib/types/toolbar-config.d.ts.map +1 -0
  46. package/dist/lib/types/toolbar-config.js +38 -0
  47. package/dist/lib/types/toolbar-config.js.map +1 -0
  48. package/dist/module.d.ts +28 -20
  49. package/dist/module.d.ts.map +1 -1
  50. package/dist/module.js +47 -74
  51. package/dist/module.js.map +1 -1
  52. package/dist/public-api.d.ts +20 -8
  53. package/dist/public-api.d.ts.map +1 -1
  54. package/dist/public-api.js +24 -9
  55. package/dist/public-api.js.map +1 -1
  56. package/package.json +20 -23
  57. package/README.md +0 -449
  58. package/dist/lib/base-field-component.d.ts +0 -152
  59. package/dist/lib/base-field-component.d.ts.map +0 -1
  60. package/dist/lib/base-field-component.js +0 -669
  61. package/dist/lib/base-field-component.js.map +0 -1
  62. package/dist/lib/base-form-context.d.ts +0 -23
  63. package/dist/lib/base-form-context.d.ts.map +0 -1
  64. package/dist/lib/base-form-context.js +0 -10
  65. package/dist/lib/base-form-context.js.map +0 -1
  66. package/dist/lib/collapsible-panel.component.d.ts +0 -68
  67. package/dist/lib/collapsible-panel.component.d.ts.map +0 -1
  68. package/dist/lib/collapsible-panel.component.js +0 -330
  69. package/dist/lib/collapsible-panel.component.js.map +0 -1
  70. package/dist/lib/form-section-controls.component.d.ts +0 -35
  71. package/dist/lib/form-section-controls.component.d.ts.map +0 -1
  72. package/dist/lib/form-section-controls.component.js +0 -273
  73. package/dist/lib/form-section-controls.component.js.map +0 -1
  74. package/dist/lib/link-field.component.d.ts +0 -62
  75. package/dist/lib/link-field.component.d.ts.map +0 -1
  76. package/dist/lib/link-field.component.js +0 -392
  77. package/dist/lib/link-field.component.js.map +0 -1
@@ -0,0 +1,1194 @@
1
+ import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ChangeDetectorRef, inject } from '@angular/core';
2
+ import { Metadata, CompositeKey, KeyValuePair, RunView } from '@memberjunction/core';
3
+ import * as i0 from "@angular/core";
4
+ import * as i1 from "@angular/common";
5
+ const _forTrack0 = ($index, $item) => $item.PrimaryKeyValue;
6
+ const _forTrack1 = ($index, $item) => $item.Message;
7
+ function MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_1_Template(rf, ctx) { if (rf & 1) {
8
+ i0.ɵɵelement(0, "label", 3);
9
+ } if (rf & 2) {
10
+ const ctx_r0 = i0.ɵɵnextContext(3);
11
+ i0.ɵɵproperty("innerHTML", ctx_r0.HighlightedDisplayName, i0.ɵɵsanitizeHtml);
12
+ } }
13
+ function MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_3_Template(rf, ctx) { if (rf & 1) {
14
+ const _r2 = i0.ɵɵgetCurrentView();
15
+ i0.ɵɵelementStart(0, "span", 5)(1, "a", 6);
16
+ i0.ɵɵlistener("click", function MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_3_Template_a_click_1_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnRecordLinkClick($event)); });
17
+ i0.ɵɵtext(2);
18
+ i0.ɵɵelementEnd()();
19
+ } if (rf & 2) {
20
+ const ctx_r0 = i0.ɵɵnextContext(3);
21
+ i0.ɵɵadvance(2);
22
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.FKDisplayName, " ");
23
+ } }
24
+ function MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_0_Template(rf, ctx) { if (rf & 1) {
25
+ const _r3 = i0.ɵɵgetCurrentView();
26
+ i0.ɵɵelementStart(0, "span", 5)(1, "a", 7);
27
+ i0.ɵɵlistener("click", function MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_0_Template_a_click_1_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnEmailLinkClick($event)); });
28
+ i0.ɵɵtext(2);
29
+ i0.ɵɵelement(3, "i", 8);
30
+ i0.ɵɵelementEnd()();
31
+ } if (rf & 2) {
32
+ const ctx_r0 = i0.ɵɵnextContext(4);
33
+ i0.ɵɵadvance(2);
34
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.FormatValue(), " ");
35
+ } }
36
+ function MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_1_Template(rf, ctx) { if (rf & 1) {
37
+ const _r4 = i0.ɵɵgetCurrentView();
38
+ i0.ɵɵelementStart(0, "span", 5)(1, "a", 9);
39
+ i0.ɵɵlistener("click", function MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_1_Template_a_click_1_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnUrlLinkClick($event)); });
40
+ i0.ɵɵtext(2);
41
+ i0.ɵɵelement(3, "i", 10);
42
+ i0.ɵɵelementEnd()();
43
+ } if (rf & 2) {
44
+ const ctx_r0 = i0.ɵɵnextContext(4);
45
+ i0.ɵɵadvance(2);
46
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.FormatValue(), " ");
47
+ } }
48
+ function MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_2_Template(rf, ctx) { if (rf & 1) {
49
+ const _r5 = i0.ɵɵgetCurrentView();
50
+ i0.ɵɵelementStart(0, "span", 5)(1, "a", 11);
51
+ i0.ɵɵlistener("click", function MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_2_Template_a_click_1_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnRecordLinkClick($event)); });
52
+ i0.ɵɵtext(2);
53
+ i0.ɵɵelement(3, "i", 10);
54
+ i0.ɵɵelementEnd()();
55
+ } if (rf & 2) {
56
+ const ctx_r0 = i0.ɵɵnextContext(4);
57
+ i0.ɵɵadvance(2);
58
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.FormatValue(), " ");
59
+ } }
60
+ function MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_3_Conditional_0_Template(rf, ctx) { if (rf & 1) {
61
+ i0.ɵɵelementStart(0, "span", 5)(1, "div", 13);
62
+ i0.ɵɵelement(2, "input", 14);
63
+ i0.ɵɵelementEnd()();
64
+ } if (rf & 2) {
65
+ const ctx_r0 = i0.ɵɵnextContext(5);
66
+ i0.ɵɵadvance(2);
67
+ i0.ɵɵproperty("checked", !!ctx_r0.Value);
68
+ } }
69
+ function MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_3_Conditional_1_Template(rf, ctx) { if (rf & 1) {
70
+ i0.ɵɵelementStart(0, "span", 12);
71
+ i0.ɵɵtext(1);
72
+ i0.ɵɵelementEnd();
73
+ } if (rf & 2) {
74
+ const ctx_r0 = i0.ɵɵnextContext(5);
75
+ i0.ɵɵadvance();
76
+ i0.ɵɵtextInterpolate(ctx_r0.FormatValue());
77
+ } }
78
+ function MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_3_Conditional_2_Template(rf, ctx) { if (rf & 1) {
79
+ i0.ɵɵelementStart(0, "span", 5);
80
+ i0.ɵɵtext(1);
81
+ i0.ɵɵelementEnd();
82
+ } if (rf & 2) {
83
+ const ctx_r0 = i0.ɵɵnextContext(5);
84
+ i0.ɵɵadvance();
85
+ i0.ɵɵtextInterpolate(ctx_r0.FormatValue());
86
+ } }
87
+ function MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_3_Template(rf, ctx) { if (rf & 1) {
88
+ i0.ɵɵconditionalCreate(0, MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_3_Conditional_0_Template, 3, 1, "span", 5)(1, MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_3_Conditional_1_Template, 2, 1, "span", 12)(2, MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_3_Conditional_2_Template, 2, 1, "span", 5);
89
+ } if (rf & 2) {
90
+ const ctx_r0 = i0.ɵɵnextContext(4);
91
+ i0.ɵɵconditional(ctx_r0.Type === "checkbox" ? 0 : ctx_r0.IsLongText ? 1 : 2);
92
+ } }
93
+ function MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Template(rf, ctx) { if (rf & 1) {
94
+ i0.ɵɵconditionalCreate(0, MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_0_Template, 4, 1, "span", 5)(1, MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_1_Template, 4, 1, "span", 5)(2, MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_2_Template, 4, 1, "span", 5)(3, MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Case_3_Template, 3, 1);
95
+ } if (rf & 2) {
96
+ let tmp_3_0;
97
+ const ctx_r0 = i0.ɵɵnextContext(3);
98
+ i0.ɵɵconditional((tmp_3_0 = ctx_r0.LinkType) === "Email" ? 0 : tmp_3_0 === "URL" ? 1 : tmp_3_0 === "Record" ? 2 : 3);
99
+ } }
100
+ function MjFormFieldComponent_Conditional_0_Conditional_0_Template(rf, ctx) { if (rf & 1) {
101
+ i0.ɵɵelementStart(0, "div", 2);
102
+ i0.ɵɵconditionalCreate(1, MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_1_Template, 1, 1, "label", 3);
103
+ i0.ɵɵelementStart(2, "div", 4);
104
+ i0.ɵɵconditionalCreate(3, MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_3_Template, 3, 1, "span", 5)(4, MjFormFieldComponent_Conditional_0_Conditional_0_Conditional_4_Template, 4, 1);
105
+ i0.ɵɵelementEnd()();
106
+ } if (rf & 2) {
107
+ const ctx_r0 = i0.ɵɵnextContext(2);
108
+ i0.ɵɵclassProp("mj-forms-field--readonly", ctx_r0.EditMode && ctx_r0.IsFieldReadOnly)("mj-forms-field--dirty", ctx_r0.IsDirty);
109
+ i0.ɵɵadvance();
110
+ i0.ɵɵconditional(ctx_r0.ShowLabel ? 1 : -1);
111
+ i0.ɵɵadvance(2);
112
+ i0.ɵɵconditional(ctx_r0.IsFKWithNameField ? 3 : 4);
113
+ } }
114
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_1_Template(rf, ctx) { if (rf & 1) {
115
+ i0.ɵɵelement(0, "label", 3);
116
+ } if (rf & 2) {
117
+ const ctx_r0 = i0.ɵɵnextContext(3);
118
+ i0.ɵɵproperty("innerHTML", ctx_r0.HighlightedDisplayName, i0.ɵɵsanitizeHtml);
119
+ } }
120
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_3_Conditional_2_Template(rf, ctx) { if (rf & 1) {
121
+ const _r7 = i0.ɵɵgetCurrentView();
122
+ i0.ɵɵelementStart(0, "button", 24);
123
+ i0.ɵɵlistener("mousedown", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_3_Conditional_2_Template_button_mousedown_0_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnFKClearClick($event)); });
124
+ i0.ɵɵelement(1, "i", 25);
125
+ i0.ɵɵelementEnd();
126
+ } }
127
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_3_Conditional_3_Template(rf, ctx) { if (rf & 1) {
128
+ i0.ɵɵelement(0, "i", 22);
129
+ } }
130
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_3_Conditional_4_For_2_Template(rf, ctx) { if (rf & 1) {
131
+ const _r8 = i0.ɵɵgetCurrentView();
132
+ i0.ɵɵelementStart(0, "div", 28);
133
+ i0.ɵɵlistener("mousedown", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_3_Conditional_4_For_2_Template_div_mousedown_0_listener($event) { const suggestion_r9 = i0.ɵɵrestoreView(_r8).$implicit; const ctx_r0 = i0.ɵɵnextContext(5); return i0.ɵɵresetView(ctx_r0.OnFKSelectItem(suggestion_r9, $event)); });
134
+ i0.ɵɵtext(1);
135
+ i0.ɵɵelementEnd();
136
+ } if (rf & 2) {
137
+ const suggestion_r9 = ctx.$implicit;
138
+ i0.ɵɵadvance();
139
+ i0.ɵɵtextInterpolate1(" ", suggestion_r9.DisplayName, " ");
140
+ } }
141
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_3_Conditional_4_Template(rf, ctx) { if (rf & 1) {
142
+ i0.ɵɵelementStart(0, "div", 26);
143
+ i0.ɵɵrepeaterCreate(1, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_3_Conditional_4_For_2_Template, 2, 1, "div", 27, _forTrack0);
144
+ i0.ɵɵelementEnd();
145
+ } if (rf & 2) {
146
+ const ctx_r0 = i0.ɵɵnextContext(4);
147
+ i0.ɵɵclassProp("mj-fk-dropdown--above", ctx_r0.OpenAbove);
148
+ i0.ɵɵproperty("ngStyle", ctx_r0.DropdownPositionStyle);
149
+ i0.ɵɵadvance();
150
+ i0.ɵɵrepeater(ctx_r0.FKSuggestions);
151
+ } }
152
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_3_Template(rf, ctx) { if (rf & 1) {
153
+ const _r6 = i0.ɵɵgetCurrentView();
154
+ i0.ɵɵelementStart(0, "div", 19)(1, "input", 20);
155
+ i0.ɵɵlistener("input", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_3_Template_input_input_1_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnFKInput($event)); })("focus", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_3_Template_input_focus_1_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnFKFocus($event)); })("blur", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_3_Template_input_blur_1_listener() { i0.ɵɵrestoreView(_r6); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnFKBlur()); });
156
+ i0.ɵɵelementEnd();
157
+ i0.ɵɵconditionalCreate(2, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_3_Conditional_2_Template, 2, 0, "button", 21)(3, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_3_Conditional_3_Template, 1, 0, "i", 22);
158
+ i0.ɵɵconditionalCreate(4, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_3_Conditional_4_Template, 3, 3, "div", 23);
159
+ i0.ɵɵelementEnd();
160
+ } if (rf & 2) {
161
+ const ctx_r0 = i0.ɵɵnextContext(3);
162
+ i0.ɵɵclassProp("mj-fk-search--matched", ctx_r0.FKIsMatched);
163
+ i0.ɵɵadvance();
164
+ i0.ɵɵproperty("value", ctx_r0.FKSearchText);
165
+ i0.ɵɵadvance();
166
+ i0.ɵɵconditional(ctx_r0.FKSearchText ? 2 : 3);
167
+ i0.ɵɵadvance(2);
168
+ i0.ɵɵconditional(ctx_r0.ShowFKDropdown && ctx_r0.FKSuggestions.length > 0 ? 4 : -1);
169
+ } }
170
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_0_Template(rf, ctx) { if (rf & 1) {
171
+ const _r10 = i0.ɵɵgetCurrentView();
172
+ i0.ɵɵelementStart(0, "input", 33);
173
+ i0.ɵɵlistener("change", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_0_Template_input_change_0_listener($event) { i0.ɵɵrestoreView(_r10); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnInputChange($event)); });
174
+ i0.ɵɵelementEnd();
175
+ } if (rf & 2) {
176
+ const ctx_r0 = i0.ɵɵnextContext(4);
177
+ i0.ɵɵproperty("value", ctx_r0.FormatValue());
178
+ } }
179
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_1_Template(rf, ctx) { if (rf & 1) {
180
+ const _r11 = i0.ɵɵgetCurrentView();
181
+ i0.ɵɵelementStart(0, "textarea", 34);
182
+ i0.ɵɵlistener("change", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_1_Template_textarea_change_0_listener($event) { i0.ɵɵrestoreView(_r11); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnInputChange($event)); });
183
+ i0.ɵɵelementEnd();
184
+ } if (rf & 2) {
185
+ const ctx_r0 = i0.ɵɵnextContext(4);
186
+ i0.ɵɵproperty("value", ctx_r0.FormatValue());
187
+ } }
188
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_2_Template(rf, ctx) { if (rf & 1) {
189
+ const _r12 = i0.ɵɵgetCurrentView();
190
+ i0.ɵɵelementStart(0, "input", 35);
191
+ i0.ɵɵlistener("change", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_2_Template_input_change_0_listener($event) { i0.ɵɵrestoreView(_r12); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnNumberChange($event)); });
192
+ i0.ɵɵelementEnd();
193
+ } if (rf & 2) {
194
+ const ctx_r0 = i0.ɵɵnextContext(4);
195
+ i0.ɵɵproperty("value", ctx_r0.Value);
196
+ } }
197
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_3_Template(rf, ctx) { if (rf & 1) {
198
+ const _r13 = i0.ɵɵgetCurrentView();
199
+ i0.ɵɵelementStart(0, "input", 36);
200
+ i0.ɵɵlistener("change", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_3_Template_input_change_0_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnDateChange($event)); });
201
+ i0.ɵɵelementEnd();
202
+ } if (rf & 2) {
203
+ const ctx_r0 = i0.ɵɵnextContext(4);
204
+ i0.ɵɵproperty("value", ctx_r0.DateInputValue);
205
+ } }
206
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_4_Template(rf, ctx) { if (rf & 1) {
207
+ const _r14 = i0.ɵɵgetCurrentView();
208
+ i0.ɵɵelementStart(0, "div", 13)(1, "input", 37);
209
+ i0.ɵɵlistener("change", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_4_Template_input_change_1_listener($event) { i0.ɵɵrestoreView(_r14); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnCheckboxChange($event)); });
210
+ i0.ɵɵelementEnd()();
211
+ } if (rf & 2) {
212
+ const ctx_r0 = i0.ɵɵnextContext(4);
213
+ i0.ɵɵadvance();
214
+ i0.ɵɵproperty("checked", !!ctx_r0.Value);
215
+ } }
216
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_5_Conditional_4_For_2_Template(rf, ctx) { if (rf & 1) {
217
+ const _r16 = i0.ɵɵgetCurrentView();
218
+ i0.ɵɵelementStart(0, "div", 28);
219
+ i0.ɵɵlistener("mousedown", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_5_Conditional_4_For_2_Template_div_mousedown_0_listener($event) { const option_r17 = i0.ɵɵrestoreView(_r16).$implicit; const ctx_r0 = i0.ɵɵnextContext(6); return i0.ɵɵresetView(ctx_r0.OnSelectOptionClick(option_r17, $event)); });
220
+ i0.ɵɵtext(1);
221
+ i0.ɵɵelementEnd();
222
+ } if (rf & 2) {
223
+ const option_r17 = ctx.$implicit;
224
+ const ctx_r0 = i0.ɵɵnextContext(6);
225
+ i0.ɵɵclassProp("mj-fk-option--selected", ctx_r0.FormatValue() === option_r17);
226
+ i0.ɵɵadvance();
227
+ i0.ɵɵtextInterpolate1(" ", option_r17, " ");
228
+ } }
229
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_5_Conditional_4_Template(rf, ctx) { if (rf & 1) {
230
+ i0.ɵɵelementStart(0, "div", 26);
231
+ i0.ɵɵrepeaterCreate(1, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_5_Conditional_4_For_2_Template, 2, 3, "div", 41, i0.ɵɵrepeaterTrackByIdentity);
232
+ i0.ɵɵelementEnd();
233
+ } if (rf & 2) {
234
+ const ctx_r0 = i0.ɵɵnextContext(5);
235
+ i0.ɵɵclassProp("mj-fk-dropdown--above", ctx_r0.OpenAbove);
236
+ i0.ɵɵproperty("ngStyle", ctx_r0.DropdownPositionStyle);
237
+ i0.ɵɵadvance();
238
+ i0.ɵɵrepeater(ctx_r0.PossibleValues);
239
+ } }
240
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_5_Template(rf, ctx) { if (rf & 1) {
241
+ const _r15 = i0.ɵɵgetCurrentView();
242
+ i0.ɵɵelementStart(0, "div", 38);
243
+ i0.ɵɵlistener("click", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_5_Template_div_click_0_listener($event) { i0.ɵɵrestoreView(_r15); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnSelectTriggerClick($event)); })("blur", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_5_Template_div_blur_0_listener() { i0.ɵɵrestoreView(_r15); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnSelectTriggerBlur()); });
244
+ i0.ɵɵelementStart(1, "span", 39);
245
+ i0.ɵɵtext(2);
246
+ i0.ɵɵelementEnd();
247
+ i0.ɵɵelement(3, "i", 40);
248
+ i0.ɵɵelementEnd();
249
+ i0.ɵɵconditionalCreate(4, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_5_Conditional_4_Template, 3, 3, "div", 23);
250
+ } if (rf & 2) {
251
+ const ctx_r0 = i0.ɵɵnextContext(4);
252
+ i0.ɵɵadvance();
253
+ i0.ɵɵclassProp("mj-custom-select-value--placeholder", !ctx_r0.FormatValue());
254
+ i0.ɵɵadvance();
255
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.FormatValue() || "-- Select --", " ");
256
+ i0.ɵɵadvance();
257
+ i0.ɵɵclassProp("mj-custom-select-chevron--open", ctx_r0.ShowSelectDropdown);
258
+ i0.ɵɵadvance();
259
+ i0.ɵɵconditional(ctx_r0.ShowSelectDropdown && ctx_r0.PossibleValues.length > 0 ? 4 : -1);
260
+ } }
261
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_6_Conditional_3_For_2_Template(rf, ctx) { if (rf & 1) {
262
+ const _r19 = i0.ɵɵgetCurrentView();
263
+ i0.ɵɵelementStart(0, "div", 28);
264
+ i0.ɵɵlistener("mousedown", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_6_Conditional_3_For_2_Template_div_mousedown_0_listener($event) { const option_r20 = i0.ɵɵrestoreView(_r19).$implicit; const ctx_r0 = i0.ɵɵnextContext(6); return i0.ɵɵresetView(ctx_r0.OnValueListSelect(option_r20, $event)); });
265
+ i0.ɵɵtext(1);
266
+ i0.ɵɵelementEnd();
267
+ } if (rf & 2) {
268
+ const option_r20 = ctx.$implicit;
269
+ i0.ɵɵadvance();
270
+ i0.ɵɵtextInterpolate1(" ", option_r20, " ");
271
+ } }
272
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_6_Conditional_3_Template(rf, ctx) { if (rf & 1) {
273
+ i0.ɵɵelementStart(0, "div", 26);
274
+ i0.ɵɵrepeaterCreate(1, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_6_Conditional_3_For_2_Template, 2, 1, "div", 27, i0.ɵɵrepeaterTrackByIdentity);
275
+ i0.ɵɵelementEnd();
276
+ } if (rf & 2) {
277
+ const ctx_r0 = i0.ɵɵnextContext(5);
278
+ i0.ɵɵclassProp("mj-fk-dropdown--above", ctx_r0.OpenAbove);
279
+ i0.ɵɵproperty("ngStyle", ctx_r0.DropdownPositionStyle);
280
+ i0.ɵɵadvance();
281
+ i0.ɵɵrepeater(ctx_r0.FilteredPossibleValues);
282
+ } }
283
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_6_Template(rf, ctx) { if (rf & 1) {
284
+ const _r18 = i0.ɵɵgetCurrentView();
285
+ i0.ɵɵelementStart(0, "div", 19)(1, "input", 42);
286
+ i0.ɵɵlistener("input", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_6_Template_input_input_1_listener($event) { i0.ɵɵrestoreView(_r18); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnValueListInput($event)); })("focus", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_6_Template_input_focus_1_listener($event) { i0.ɵɵrestoreView(_r18); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnValueListFocus($event)); })("blur", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_6_Template_input_blur_1_listener() { i0.ɵɵrestoreView(_r18); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnValueListBlur()); });
287
+ i0.ɵɵelementEnd();
288
+ i0.ɵɵelement(2, "i", 22);
289
+ i0.ɵɵconditionalCreate(3, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_6_Conditional_3_Template, 3, 3, "div", 23);
290
+ i0.ɵɵelementEnd();
291
+ } if (rf & 2) {
292
+ const ctx_r0 = i0.ɵɵnextContext(4);
293
+ i0.ɵɵadvance();
294
+ i0.ɵɵproperty("value", ctx_r0.FormatValue());
295
+ i0.ɵɵadvance(2);
296
+ i0.ɵɵconditional(ctx_r0.ShowValueListDropdown && ctx_r0.FilteredPossibleValues.length > 0 ? 3 : -1);
297
+ } }
298
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_7_Template(rf, ctx) { if (rf & 1) {
299
+ const _r21 = i0.ɵɵgetCurrentView();
300
+ i0.ɵɵelementStart(0, "input", 33);
301
+ i0.ɵɵlistener("change", function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_7_Template_input_change_0_listener($event) { i0.ɵɵrestoreView(_r21); const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.OnInputChange($event)); });
302
+ i0.ɵɵelementEnd();
303
+ } if (rf & 2) {
304
+ const ctx_r0 = i0.ɵɵnextContext(4);
305
+ i0.ɵɵproperty("value", ctx_r0.FormatValue());
306
+ } }
307
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Template(rf, ctx) { if (rf & 1) {
308
+ i0.ɵɵconditionalCreate(0, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_0_Template, 1, 1, "input", 29)(1, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_1_Template, 1, 1, "textarea", 30)(2, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_2_Template, 1, 1, "input", 31)(3, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_3_Template, 1, 1, "input", 32)(4, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_4_Template, 2, 1, "div", 13)(5, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_5_Template, 5, 6)(6, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_6_Template, 4, 2, "div", 19)(7, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Case_7_Template, 1, 1, "input", 29);
309
+ } if (rf & 2) {
310
+ let tmp_3_0;
311
+ const ctx_r0 = i0.ɵɵnextContext(3);
312
+ i0.ɵɵconditional((tmp_3_0 = ctx_r0.Type) === "textbox" ? 0 : tmp_3_0 === "textarea" ? 1 : tmp_3_0 === "number" ? 2 : tmp_3_0 === "datepicker" ? 3 : tmp_3_0 === "checkbox" ? 4 : tmp_3_0 === "select" ? 5 : tmp_3_0 === "autocomplete" ? 6 : 7);
313
+ } }
314
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_5_For_2_Template(rf, ctx) { if (rf & 1) {
315
+ i0.ɵɵelementStart(0, "div", 43);
316
+ i0.ɵɵelement(1, "i", 44);
317
+ i0.ɵɵelementStart(2, "span");
318
+ i0.ɵɵtext(3);
319
+ i0.ɵɵelementEnd()();
320
+ } if (rf & 2) {
321
+ const error_r22 = ctx.$implicit;
322
+ i0.ɵɵadvance(3);
323
+ i0.ɵɵtextInterpolate(error_r22.Message);
324
+ } }
325
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_5_Template(rf, ctx) { if (rf & 1) {
326
+ i0.ɵɵelementStart(0, "div", 17);
327
+ i0.ɵɵrepeaterCreate(1, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_5_For_2_Template, 4, 1, "div", 43, _forTrack1);
328
+ i0.ɵɵelementEnd();
329
+ } if (rf & 2) {
330
+ const ctx_r0 = i0.ɵɵnextContext(3);
331
+ i0.ɵɵadvance();
332
+ i0.ɵɵrepeater(ctx_r0.DisplayErrors);
333
+ } }
334
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_6_For_2_Template(rf, ctx) { if (rf & 1) {
335
+ i0.ɵɵelementStart(0, "div", 43);
336
+ i0.ɵɵelement(1, "i", 45);
337
+ i0.ɵɵelementStart(2, "span");
338
+ i0.ɵɵtext(3);
339
+ i0.ɵɵelementEnd()();
340
+ } if (rf & 2) {
341
+ const warning_r23 = ctx.$implicit;
342
+ i0.ɵɵadvance(3);
343
+ i0.ɵɵtextInterpolate(warning_r23.Message);
344
+ } }
345
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_6_Template(rf, ctx) { if (rf & 1) {
346
+ i0.ɵɵelementStart(0, "div", 18);
347
+ i0.ɵɵrepeaterCreate(1, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_6_For_2_Template, 4, 1, "div", 43, _forTrack1);
348
+ i0.ɵɵelementEnd();
349
+ } if (rf & 2) {
350
+ const ctx_r0 = i0.ɵɵnextContext(3);
351
+ i0.ɵɵadvance();
352
+ i0.ɵɵrepeater(ctx_r0.DisplayWarnings);
353
+ } }
354
+ function MjFormFieldComponent_Conditional_0_Conditional_1_Template(rf, ctx) { if (rf & 1) {
355
+ i0.ɵɵelementStart(0, "div", 15);
356
+ i0.ɵɵconditionalCreate(1, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_1_Template, 1, 1, "label", 3);
357
+ i0.ɵɵelementStart(2, "div", 4);
358
+ i0.ɵɵconditionalCreate(3, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_3_Template, 5, 5, "div", 16)(4, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_4_Template, 8, 1);
359
+ i0.ɵɵelementEnd();
360
+ i0.ɵɵconditionalCreate(5, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_5_Template, 3, 0, "div", 17);
361
+ i0.ɵɵconditionalCreate(6, MjFormFieldComponent_Conditional_0_Conditional_1_Conditional_6_Template, 3, 0, "div", 18);
362
+ i0.ɵɵelementEnd();
363
+ } if (rf & 2) {
364
+ const ctx_r0 = i0.ɵɵnextContext(2);
365
+ i0.ɵɵclassProp("mj-forms-field--dirty", ctx_r0.IsDirty)("mj-forms-field--required-empty", ctx_r0.IsRequiredEmpty)("mj-forms-field--has-error", ctx_r0.ShowErrors)("mj-forms-field--has-warning", ctx_r0.ShowWarnings);
366
+ i0.ɵɵadvance();
367
+ i0.ɵɵconditional(ctx_r0.ShowLabel ? 1 : -1);
368
+ i0.ɵɵadvance(2);
369
+ i0.ɵɵconditional(ctx_r0.HasRelatedEntity ? 3 : 4);
370
+ i0.ɵɵadvance(2);
371
+ i0.ɵɵconditional(ctx_r0.DisplayErrors.length > 0 ? 5 : -1);
372
+ i0.ɵɵadvance();
373
+ i0.ɵɵconditional(ctx_r0.DisplayWarnings.length > 0 ? 6 : -1);
374
+ } }
375
+ function MjFormFieldComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
376
+ i0.ɵɵconditionalCreate(0, MjFormFieldComponent_Conditional_0_Conditional_0_Template, 5, 6, "div", 0);
377
+ i0.ɵɵconditionalCreate(1, MjFormFieldComponent_Conditional_0_Conditional_1_Template, 7, 12, "div", 1);
378
+ } if (rf & 2) {
379
+ const ctx_r0 = i0.ɵɵnextContext();
380
+ i0.ɵɵconditional((!ctx_r0.EditMode || ctx_r0.IsFieldReadOnly) && !ctx_r0.ShouldHideField ? 0 : -1);
381
+ i0.ɵɵadvance();
382
+ i0.ɵɵconditional(ctx_r0.EditMode && !ctx_r0.IsFieldReadOnly ? 1 : -1);
383
+ } }
384
+ /**
385
+ * Modern form field component that renders entity fields in both read-only and edit modes.
386
+ *
387
+ * **Read mode**: Clean text display with optional hyperlinks (email, URL, record).
388
+ * **Edit mode**: Native HTML inputs with custom-built select and autocomplete components.
389
+ *
390
+ * Uses:
391
+ * - Native `<input>` for text, number, date
392
+ * - Custom styled dropdown for value list selections
393
+ * - Custom autocomplete with floating dropdown for foreign key search
394
+ * - Custom-styled `<input type="checkbox">` for booleans
395
+ *
396
+ * All navigation (clicking FK links, email links, URL links) is emitted as events.
397
+ *
398
+ * @example
399
+ * ```html
400
+ * <mj-form-field
401
+ * [Record]="record"
402
+ * [EditMode]="editMode"
403
+ * FieldName="CustomerName"
404
+ * Type="textbox"
405
+ * [FormContext]="formContext"
406
+ * (Navigate)="onNavigate($event)"
407
+ * (ValueChange)="onValueChange($event)">
408
+ * </mj-form-field>
409
+ * ```
410
+ */
411
+ export class MjFormFieldComponent {
412
+ cdr = inject(ChangeDetectorRef);
413
+ /** The entity record containing this field */
414
+ Record;
415
+ /** Whether the form is in edit mode */
416
+ EditMode = false;
417
+ /** The field code name (must match a field on the entity) */
418
+ FieldName = '';
419
+ /**
420
+ * The control type to render in edit mode.
421
+ * Determines which input component is used.
422
+ *
423
+ * Also accepts deprecated Kendo-style type names for backward compatibility:
424
+ * - `'dropdownlist'` → `'select'`
425
+ * - `'numerictextbox'` → `'number'`
426
+ */
427
+ set Type(value) {
428
+ // Normalize deprecated Kendo type names to modern equivalents
429
+ switch (value) {
430
+ case 'dropdownlist':
431
+ this._type = 'select';
432
+ break;
433
+ case 'numerictextbox':
434
+ this._type = 'number';
435
+ break;
436
+ default:
437
+ this._type = value;
438
+ break;
439
+ }
440
+ }
441
+ get Type() {
442
+ return this._type;
443
+ }
444
+ _type = 'textbox';
445
+ /**
446
+ * Link type for read-only rendering.
447
+ * - 'Email': renders as mailto link
448
+ * - 'URL': renders as external hyperlink
449
+ * - 'Record': renders as FK link to related entity record
450
+ * - 'None': renders as plain text
451
+ */
452
+ LinkType = 'None';
453
+ /** Whether to show the field label */
454
+ ShowLabel = true;
455
+ /** Form-level context (search filter, showEmptyFields) */
456
+ FormContext;
457
+ // ---- Deprecated camelCase aliases (backward compat) ----
458
+ /** @deprecated Use [Record] instead */
459
+ set _deprecatedRecord(value) { this.Record = value; }
460
+ /** @deprecated Use [FormContext] instead */
461
+ set _deprecatedFormContext(value) { this.FormContext = value; }
462
+ /** Whether to hide this field when empty in read-only mode. Default: true */
463
+ HideWhenEmptyInReadOnlyMode = true;
464
+ /** Override display name (defaults to entity field metadata DisplayName) */
465
+ DisplayNameOverride = '';
466
+ /** Override possible values for select/autocomplete */
467
+ PossibleValuesOverride = null;
468
+ /** Emitted when the field value changes */
469
+ ValueChange = new EventEmitter();
470
+ /** Emitted when a navigation action is triggered (FK link click, email click, etc.) */
471
+ Navigate = new EventEmitter();
472
+ // ---- Computed Properties ----
473
+ /** Get EntityFieldInfo metadata for this field */
474
+ get FieldInfo() {
475
+ return this.Record?.EntityInfo?.Fields?.find(f => f.Name === this.FieldName);
476
+ }
477
+ /** Display name from metadata or override */
478
+ get DisplayName() {
479
+ if (this.DisplayNameOverride)
480
+ return this.DisplayNameOverride;
481
+ // Friendly labels for standard MJ timestamp fields
482
+ if (this.FieldName === '__mj_CreatedAt')
483
+ return 'Created';
484
+ if (this.FieldName === '__mj_UpdatedAt')
485
+ return 'Updated';
486
+ // For FK fields that have a mapped name field, use the name field's
487
+ // friendly label (e.g., show "Parent" instead of "Parent ID")
488
+ const nameFieldMap = this.FieldInfo?.RelatedEntityNameFieldMap;
489
+ if (nameFieldMap) {
490
+ const nameField = this.Record?.EntityInfo?.Fields?.find(f => f.Name === nameFieldMap);
491
+ if (nameField) {
492
+ return nameField.DisplayNameOrName;
493
+ }
494
+ }
495
+ return this.FieldInfo?.DisplayNameOrName ?? this.FieldName;
496
+ }
497
+ /** Display name with search highlighting applied */
498
+ get HighlightedDisplayName() {
499
+ const filter = this.FormContext?.sectionFilter?.trim();
500
+ if (!filter)
501
+ return this.DisplayName;
502
+ const escaped = filter.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
503
+ const regex = new RegExp(escaped, 'gi');
504
+ return this.DisplayName.replace(regex, '<mark class="mj-forms-search-highlight">$&</mark>');
505
+ }
506
+ /** Current field value */
507
+ get Value() {
508
+ return this.Record?.Get(this.FieldName);
509
+ }
510
+ /** Set field value, mark touched, run validation, and emit change event */
511
+ set Value(newValue) {
512
+ if (!this.Record)
513
+ return;
514
+ const oldValue = this.Record.Get(this.FieldName);
515
+ this.Record.Set(this.FieldName, newValue);
516
+ this._touched = true;
517
+ this.runFieldValidation();
518
+ this.ValueChange.emit({ FieldName: this.FieldName, OldValue: oldValue, NewValue: newValue });
519
+ }
520
+ /** Whether this field is read-only based on entity metadata */
521
+ get IsFieldReadOnly() {
522
+ if (!this.FieldInfo)
523
+ return false;
524
+ return this.FieldInfo.ReadOnly;
525
+ }
526
+ /** Whether this field has been modified (dirty). Only shown for saved records (not new). */
527
+ get IsDirty() {
528
+ if (!this.Record || !this.Record.IsSaved)
529
+ return false;
530
+ const field = this.Record.Fields?.find(f => f.Name === this.FieldName);
531
+ return field?.Dirty ?? false;
532
+ }
533
+ /** Whether this field is required (not nullable) */
534
+ get IsRequired() {
535
+ return this.FieldInfo?.AllowsNull === false;
536
+ }
537
+ /** Whether this is a required field that is currently empty (for validation styling) */
538
+ get IsRequiredEmpty() {
539
+ // Defer to the validation system when it has active errors for this field
540
+ if (this.ShowErrors)
541
+ return false;
542
+ if (!this.IsRequired || !this.EditMode)
543
+ return false;
544
+ const val = this.Value;
545
+ return val === null || val === undefined || val === '';
546
+ }
547
+ // ---- INLINE VALIDATION ----
548
+ /** Whether the user has interacted with (changed) this field */
549
+ _touched = false;
550
+ /** Locally computed validation errors for this field */
551
+ _fieldErrors = [];
552
+ /** Validation errors for this field from the best available source */
553
+ get FieldErrors() {
554
+ // If touched, use fresh local validation (covers real-time feedback)
555
+ if (this._touched) {
556
+ return this._fieldErrors;
557
+ }
558
+ // Otherwise use form-level errors from save failure (covers untouched fields)
559
+ const contextErrors = this.FormContext?.validationErrors;
560
+ if (contextErrors && contextErrors.length > 0) {
561
+ return contextErrors.filter(e => e.Source === this.FieldName);
562
+ }
563
+ return [];
564
+ }
565
+ /** Whether to show validation messages (touched OR form-level showValidation) */
566
+ get ShowValidation() {
567
+ if (!this.EditMode || this.IsFieldReadOnly)
568
+ return false;
569
+ return this._touched || (this.FormContext?.showValidation === true);
570
+ }
571
+ /** Whether this field has active error-level validation failures to display */
572
+ get ShowErrors() {
573
+ return this.ShowValidation && this.FieldErrors.some(e => e.Type === 'Failure');
574
+ }
575
+ /** Whether this field has active warning-level validation issues to display */
576
+ get ShowWarnings() {
577
+ return this.ShowValidation && !this.ShowErrors && this.FieldErrors.some(e => e.Type === 'Warning');
578
+ }
579
+ /** Error messages to render in the template */
580
+ get DisplayErrors() {
581
+ if (!this.ShowValidation)
582
+ return [];
583
+ return this.FieldErrors.filter(e => e.Type === 'Failure');
584
+ }
585
+ /** Warning messages to render in the template */
586
+ get DisplayWarnings() {
587
+ if (!this.ShowValidation)
588
+ return [];
589
+ return this.FieldErrors.filter(e => e.Type === 'Warning');
590
+ }
591
+ /**
592
+ * Runs validation for this field using the full Record.Validate() method.
593
+ * This captures both built-in field checks and custom Validate() overrides
594
+ * (including cross-field rules). Results are filtered to this field only.
595
+ */
596
+ runFieldValidation() {
597
+ if (!this.Record || !this.EditMode) {
598
+ this._fieldErrors = [];
599
+ return;
600
+ }
601
+ try {
602
+ const result = this.Record.Validate();
603
+ this._fieldErrors = result.Errors.filter(e => e.Source === this.FieldName);
604
+ }
605
+ catch {
606
+ this._fieldErrors = [];
607
+ }
608
+ this.cdr.markForCheck();
609
+ }
610
+ /** Whether this field should be hidden (empty in read-only mode) */
611
+ get ShouldHideField() {
612
+ if (this.EditMode)
613
+ return false;
614
+ if (!this.HideWhenEmptyInReadOnlyMode)
615
+ return false;
616
+ if (this.FormContext?.showEmptyFields)
617
+ return false;
618
+ const val = this.Value;
619
+ return val === null || val === undefined || val === '';
620
+ }
621
+ /** Possible values for select/autocomplete from entity metadata or override */
622
+ get PossibleValues() {
623
+ if (this.PossibleValuesOverride)
624
+ return this.PossibleValuesOverride;
625
+ const values = this.FieldInfo?.EntityFieldValues;
626
+ if (values && values.length > 0) {
627
+ return values.map(v => v.Value);
628
+ }
629
+ return [];
630
+ }
631
+ /** Whether this field has a related entity (foreign key) */
632
+ get HasRelatedEntity() {
633
+ const re = this.FieldInfo?.RelatedEntity;
634
+ return re != null && re.length > 0;
635
+ }
636
+ /**
637
+ * For FK fields, returns the human-readable name to display instead of the raw ID.
638
+ *
639
+ * Resolution order:
640
+ * 1. If `RelatedEntityNameFieldMap` exists, read the name from the joined field on the record (sync).
641
+ * 2. Otherwise, fall back to an async `GetEntityRecordName` lookup (result cached in `_resolvedFKName`).
642
+ */
643
+ get FKDisplayName() {
644
+ if (!this.Record || !this.HasRelatedEntity)
645
+ return null;
646
+ // Fast path: name field is joined into our record's view
647
+ const nameFieldMap = this.FieldInfo?.RelatedEntityNameFieldMap;
648
+ if (nameFieldMap) {
649
+ const val = this.Record.Get(nameFieldMap);
650
+ if (val != null && val !== '')
651
+ return String(val);
652
+ }
653
+ // Async fallback: resolved via GetEntityRecordName
654
+ if (this._resolvedFKName !== undefined) {
655
+ return this._resolvedFKName;
656
+ }
657
+ // Kick off async resolution if not already in flight
658
+ this.resolveFKNameAsync();
659
+ return null; // will render once resolved
660
+ }
661
+ /**
662
+ * Whether this FK field should display the related entity name
663
+ * with a hyperlink instead of the raw ID value.
664
+ */
665
+ get IsFKWithNameField() {
666
+ return this.HasRelatedEntity && this.FKDisplayName != null;
667
+ }
668
+ // ---- Async FK name resolution ----
669
+ /** Cached result from GetEntityRecordName. undefined = not yet resolved. */
670
+ _resolvedFKName = undefined;
671
+ /** Tracks which FK value we resolved so we can invalidate on record change */
672
+ _resolvedFKValue = undefined;
673
+ /** Whether an async lookup is in flight */
674
+ _fkNameLoading = false;
675
+ /**
676
+ * Asynchronously resolves the display name for an FK field that lacks
677
+ * a RelatedEntityNameFieldMap by calling Metadata.GetEntityRecordName.
678
+ */
679
+ resolveFKNameAsync() {
680
+ if (this._fkNameLoading)
681
+ return;
682
+ const fkValue = this.Value;
683
+ if (fkValue == null || fkValue === '')
684
+ return;
685
+ const fieldInfo = this.FieldInfo;
686
+ if (!fieldInfo?.RelatedEntity || !fieldInfo?.RelatedEntityFieldName)
687
+ return;
688
+ this._fkNameLoading = true;
689
+ this._resolvedFKValue = fkValue;
690
+ const pk = new CompositeKey([{
691
+ FieldName: fieldInfo.RelatedEntityFieldName,
692
+ Value: fkValue
693
+ }]);
694
+ const md = new Metadata();
695
+ md.GetEntityRecordName(fieldInfo.RelatedEntity, pk)
696
+ .then(name => {
697
+ this._resolvedFKName = name || null;
698
+ this._fkNameLoading = false;
699
+ this.cdr.markForCheck();
700
+ })
701
+ .catch(() => {
702
+ this._resolvedFKName = null;
703
+ this._fkNameLoading = false;
704
+ });
705
+ }
706
+ // ============================================
707
+ // DROPDOWN POSITIONING (shared by FK, value list, select)
708
+ // ============================================
709
+ /** Calculated position for fixed-position dropdowns */
710
+ DropdownTop = 0;
711
+ DropdownLeft = 0;
712
+ DropdownWidth = 0;
713
+ OpenAbove = false;
714
+ /** Cleanup function for scroll/resize listeners */
715
+ _scrollCleanup = null;
716
+ /** Inline style for the fixed-position dropdown */
717
+ get DropdownPositionStyle() {
718
+ if (this.OpenAbove) {
719
+ return {
720
+ 'position': 'fixed',
721
+ 'bottom': (window.innerHeight - this.DropdownTop) + 'px',
722
+ 'top': 'auto',
723
+ 'left': this.DropdownLeft + 'px',
724
+ 'width': this.DropdownWidth + 'px',
725
+ 'z-index': '10000'
726
+ };
727
+ }
728
+ return {
729
+ 'position': 'fixed',
730
+ 'top': this.DropdownTop + 'px',
731
+ 'bottom': 'auto',
732
+ 'left': this.DropdownLeft + 'px',
733
+ 'width': this.DropdownWidth + 'px',
734
+ 'z-index': '10000'
735
+ };
736
+ }
737
+ /** Calculate dropdown position relative to viewport from anchor element */
738
+ updateDropdownPosition(anchorEl) {
739
+ const rect = anchorEl.getBoundingClientRect();
740
+ const dropdownMaxHeight = 240;
741
+ const gap = 4;
742
+ const spaceBelow = window.innerHeight - rect.bottom;
743
+ const spaceAbove = rect.top;
744
+ if (spaceBelow < dropdownMaxHeight && spaceAbove > spaceBelow) {
745
+ this.OpenAbove = true;
746
+ this.DropdownTop = rect.top - gap;
747
+ }
748
+ else {
749
+ this.OpenAbove = false;
750
+ this.DropdownTop = rect.bottom + gap;
751
+ }
752
+ this.DropdownLeft = rect.left;
753
+ this.DropdownWidth = rect.width;
754
+ }
755
+ /** Start listening for scroll/resize to close dropdowns */
756
+ startScrollListener() {
757
+ this.stopScrollListener();
758
+ const onScroll = () => {
759
+ this.closeAllDropdowns();
760
+ this.cdr.markForCheck();
761
+ };
762
+ const onResize = () => {
763
+ this.closeAllDropdowns();
764
+ this.cdr.markForCheck();
765
+ };
766
+ document.addEventListener('scroll', onScroll, true); // capture phase catches all scrollable ancestors
767
+ window.addEventListener('resize', onResize);
768
+ this._scrollCleanup = () => {
769
+ document.removeEventListener('scroll', onScroll, true);
770
+ window.removeEventListener('resize', onResize);
771
+ };
772
+ }
773
+ /** Remove scroll/resize listeners */
774
+ stopScrollListener() {
775
+ if (this._scrollCleanup) {
776
+ this._scrollCleanup();
777
+ this._scrollCleanup = null;
778
+ }
779
+ }
780
+ /** Close all dropdown types */
781
+ closeAllDropdowns() {
782
+ this.ShowFKDropdown = false;
783
+ this.ShowValueListDropdown = false;
784
+ this.ShowSelectDropdown = false;
785
+ this.stopScrollListener();
786
+ }
787
+ // ============================================
788
+ // FK AUTOCOMPLETE (custom implementation)
789
+ // ============================================
790
+ /** Suggestions returned from the FK entity search */
791
+ FKSuggestions = [];
792
+ /** Whether the current FK value is a confirmed match (user selected from dropdown) */
793
+ FKIsMatched = false;
794
+ /** Whether the FK dropdown is visible */
795
+ ShowFKDropdown = false;
796
+ /** Text the user is currently typing in the FK search input. null = use display name. */
797
+ _fkInputText = null;
798
+ /** Debounce timer for FK search */
799
+ _fkSearchTimeout = null;
800
+ /** Last known FK input element for position recalculation */
801
+ _lastFKInputEl = null;
802
+ /** The display text shown in the FK search input */
803
+ get FKSearchText() {
804
+ if (this._fkInputText !== null)
805
+ return this._fkInputText;
806
+ return this.FKDisplayName || '';
807
+ }
808
+ /** Handle typing in the FK search input */
809
+ OnFKInput(event) {
810
+ const input = event.target;
811
+ this._fkInputText = input.value;
812
+ this._lastFKInputEl = input;
813
+ this.FKIsMatched = false;
814
+ // Debounce search
815
+ if (this._fkSearchTimeout)
816
+ clearTimeout(this._fkSearchTimeout);
817
+ this._fkSearchTimeout = setTimeout(() => {
818
+ if (this._fkInputText && this._fkInputText.length >= 1) {
819
+ this.searchRelatedEntity(this._fkInputText);
820
+ }
821
+ else {
822
+ this.FKSuggestions = [];
823
+ this.ShowFKDropdown = false;
824
+ this.stopScrollListener();
825
+ this.cdr.markForCheck();
826
+ }
827
+ }, 300);
828
+ }
829
+ /** Show dropdown on focus if suggestions exist */
830
+ OnFKFocus(event) {
831
+ const input = event.target;
832
+ this._lastFKInputEl = input;
833
+ if (this.FKSuggestions.length > 0) {
834
+ this.updateDropdownPosition(input);
835
+ this.startScrollListener();
836
+ this.ShowFKDropdown = true;
837
+ this.cdr.markForCheck();
838
+ }
839
+ }
840
+ /** Hide dropdown on blur, revert to matched name if user didn't select */
841
+ OnFKBlur() {
842
+ // Small delay so mousedown on dropdown items fires first
843
+ setTimeout(() => {
844
+ this.ShowFKDropdown = false;
845
+ this.stopScrollListener();
846
+ // If user was typing but didn't select, revert to display name
847
+ if (!this.FKIsMatched && this._fkInputText !== null) {
848
+ this._fkInputText = null;
849
+ }
850
+ this.cdr.markForCheck();
851
+ }, 200);
852
+ }
853
+ /** Select an item from the FK dropdown */
854
+ OnFKSelectItem(suggestion, event) {
855
+ event.preventDefault();
856
+ this._fkInputText = suggestion.DisplayName;
857
+ this.Value = suggestion.PrimaryKeyValue;
858
+ this.FKIsMatched = true;
859
+ this.ShowFKDropdown = false;
860
+ this.stopScrollListener();
861
+ // Update virtual name field for visual consistency after save
862
+ const nameFieldMap = this.FieldInfo?.RelatedEntityNameFieldMap;
863
+ if (nameFieldMap) {
864
+ this.Record.Set(nameFieldMap, suggestion.DisplayName);
865
+ }
866
+ this.cdr.markForCheck();
867
+ }
868
+ /** Clear FK value via the X button */
869
+ OnFKClearClick(event) {
870
+ event.preventDefault();
871
+ this.Value = null;
872
+ this._fkInputText = null;
873
+ this.FKIsMatched = false;
874
+ this.FKSuggestions = [];
875
+ this.ShowFKDropdown = false;
876
+ this.stopScrollListener();
877
+ this._resolvedFKName = undefined;
878
+ this._resolvedFKValue = undefined;
879
+ const nameFieldMap = this.FieldInfo?.RelatedEntityNameFieldMap;
880
+ if (nameFieldMap) {
881
+ this.Record.Set(nameFieldMap, '');
882
+ }
883
+ this.cdr.markForCheck();
884
+ }
885
+ /**
886
+ * Perform a RunView search on the related entity's name field.
887
+ */
888
+ async searchRelatedEntity(query) {
889
+ const fieldInfo = this.FieldInfo;
890
+ if (!fieldInfo?.RelatedEntity)
891
+ return;
892
+ const md = new Metadata();
893
+ const relatedEntity = md.Entities.find(e => e.Name === fieldInfo.RelatedEntity);
894
+ if (!relatedEntity?.NameField)
895
+ return;
896
+ const nameFieldName = relatedEntity.NameField.Name;
897
+ const pkFieldName = fieldInfo.RelatedEntityFieldName || 'ID';
898
+ const escapedQuery = query.replace(/'/g, "''");
899
+ const rv = new RunView();
900
+ const result = await rv.RunView({
901
+ EntityName: fieldInfo.RelatedEntity,
902
+ ExtraFilter: `[${nameFieldName}] LIKE '%${escapedQuery}%'`,
903
+ MaxRows: 20,
904
+ ResultType: 'simple',
905
+ Fields: [pkFieldName, nameFieldName]
906
+ });
907
+ if (result.Success) {
908
+ this.FKSuggestions = result.Results.map(r => ({
909
+ PrimaryKeyValue: r[pkFieldName],
910
+ DisplayName: String(r[nameFieldName] || '')
911
+ }));
912
+ if (this.FKSuggestions.length > 0 && this._lastFKInputEl) {
913
+ this.updateDropdownPosition(this._lastFKInputEl);
914
+ this.startScrollListener();
915
+ }
916
+ this.ShowFKDropdown = this.FKSuggestions.length > 0;
917
+ }
918
+ else {
919
+ this.FKSuggestions = [];
920
+ this.ShowFKDropdown = false;
921
+ this.stopScrollListener();
922
+ }
923
+ this.cdr.markForCheck();
924
+ }
925
+ // ============================================
926
+ // VALUE LIST AUTOCOMPLETE (for 'autocomplete' type)
927
+ // ============================================
928
+ /** Whether the value list dropdown is visible */
929
+ ShowValueListDropdown = false;
930
+ /** Current filter text for the value list */
931
+ _valueListFilter = '';
932
+ /** Filtered possible values based on what the user typed */
933
+ get FilteredPossibleValues() {
934
+ if (!this._valueListFilter)
935
+ return this.PossibleValues;
936
+ const filter = this._valueListFilter.toLowerCase();
937
+ return this.PossibleValues.filter(v => v.toLowerCase().includes(filter));
938
+ }
939
+ /** Handle typing in the value list autocomplete */
940
+ OnValueListInput(event) {
941
+ const input = event.target;
942
+ this._valueListFilter = input.value;
943
+ this.Value = input.value;
944
+ this.updateDropdownPosition(input);
945
+ if (!this._scrollCleanup)
946
+ this.startScrollListener();
947
+ this.ShowValueListDropdown = true;
948
+ this.cdr.markForCheck();
949
+ }
950
+ /** Show dropdown on focus */
951
+ OnValueListFocus(event) {
952
+ const input = event.target;
953
+ this.updateDropdownPosition(input);
954
+ this.startScrollListener();
955
+ this.ShowValueListDropdown = true;
956
+ this.cdr.markForCheck();
957
+ }
958
+ /** Hide dropdown on blur */
959
+ OnValueListBlur() {
960
+ setTimeout(() => {
961
+ this.ShowValueListDropdown = false;
962
+ this.stopScrollListener();
963
+ this.cdr.markForCheck();
964
+ }, 200);
965
+ }
966
+ /** Select an item from the value list dropdown */
967
+ OnValueListSelect(value, event) {
968
+ event.preventDefault();
969
+ this.Value = value;
970
+ this._valueListFilter = '';
971
+ this.ShowValueListDropdown = false;
972
+ this.stopScrollListener();
973
+ this.cdr.markForCheck();
974
+ }
975
+ // ============================================
976
+ // CUSTOM SELECT DROPDOWN (replaces native <select>)
977
+ // ============================================
978
+ /** Whether the custom select dropdown is visible */
979
+ ShowSelectDropdown = false;
980
+ /** Toggle the custom select dropdown */
981
+ OnSelectTriggerClick(event) {
982
+ event.preventDefault();
983
+ if (this.ShowSelectDropdown) {
984
+ this.ShowSelectDropdown = false;
985
+ this.stopScrollListener();
986
+ }
987
+ else {
988
+ const el = event.currentTarget;
989
+ this.updateDropdownPosition(el);
990
+ this.startScrollListener();
991
+ this.ShowSelectDropdown = true;
992
+ }
993
+ this.cdr.markForCheck();
994
+ }
995
+ /** Close the select dropdown on blur */
996
+ OnSelectTriggerBlur() {
997
+ setTimeout(() => {
998
+ this.ShowSelectDropdown = false;
999
+ this.stopScrollListener();
1000
+ this.cdr.markForCheck();
1001
+ }, 200);
1002
+ }
1003
+ /** Select an option from the custom select dropdown */
1004
+ OnSelectOptionClick(value, event) {
1005
+ event.preventDefault();
1006
+ this.Value = value;
1007
+ this.ShowSelectDropdown = false;
1008
+ this.stopScrollListener();
1009
+ this.cdr.markForCheck();
1010
+ }
1011
+ // ============================================
1012
+ // GENERAL PROPERTIES
1013
+ // ============================================
1014
+ /** Whether the field's varchar(max) - long text that gets markdown rendering */
1015
+ get IsLongText() {
1016
+ return this.FieldInfo?.Length === -1;
1017
+ }
1018
+ /** Format a value for display */
1019
+ FormatValue() {
1020
+ const val = this.Value;
1021
+ if (val === null || val === undefined)
1022
+ return '';
1023
+ if (val instanceof Date) {
1024
+ return val.toLocaleString();
1025
+ }
1026
+ return String(val);
1027
+ }
1028
+ ngOnChanges(changes) {
1029
+ if (changes['FormContext'] || changes['EditMode'] || changes['Record']) {
1030
+ // Reset FK state when record changes
1031
+ if (changes['Record']) {
1032
+ this._fkInputText = null;
1033
+ this.FKIsMatched = false;
1034
+ this.FKSuggestions = [];
1035
+ this.ShowFKDropdown = false;
1036
+ this._resolvedFKName = undefined;
1037
+ this._resolvedFKValue = undefined;
1038
+ this._fkNameLoading = false;
1039
+ // Reset validation state for new record
1040
+ this._touched = false;
1041
+ this._fieldErrors = [];
1042
+ }
1043
+ // Reset validation state when exiting edit mode
1044
+ if (changes['EditMode'] && !this.EditMode) {
1045
+ this._touched = false;
1046
+ this._fieldErrors = [];
1047
+ }
1048
+ this.cdr.markForCheck();
1049
+ }
1050
+ }
1051
+ ngOnDestroy() {
1052
+ if (this._fkSearchTimeout) {
1053
+ clearTimeout(this._fkSearchTimeout);
1054
+ }
1055
+ this.stopScrollListener();
1056
+ }
1057
+ // ---- Navigation Handlers ----
1058
+ /**
1059
+ * Handle click on a record FK link in read-only mode.
1060
+ */
1061
+ OnRecordLinkClick(event) {
1062
+ event.preventDefault();
1063
+ event.stopPropagation();
1064
+ if (!this.FieldInfo || !this.Record)
1065
+ return;
1066
+ const relatedEntityName = this.FieldInfo.RelatedEntity;
1067
+ if (!relatedEntityName)
1068
+ return;
1069
+ const fkValue = this.Value;
1070
+ if (!fkValue)
1071
+ return;
1072
+ // Build a CompositeKey for the FK target record using the related entity's PK field
1073
+ const relatedFieldName = this.FieldInfo.RelatedEntityFieldName || 'ID';
1074
+ const targetKey = new CompositeKey([new KeyValuePair(relatedFieldName, fkValue)]);
1075
+ this.Navigate.emit({
1076
+ Kind: 'record',
1077
+ EntityName: relatedEntityName,
1078
+ PrimaryKey: targetKey,
1079
+ OpenInNewTab: event.ctrlKey || event.metaKey
1080
+ });
1081
+ }
1082
+ /**
1083
+ * Handle click on an email link in read-only mode.
1084
+ */
1085
+ OnEmailLinkClick(event) {
1086
+ event.preventDefault();
1087
+ event.stopPropagation();
1088
+ const email = this.FormatValue();
1089
+ if (!email)
1090
+ return;
1091
+ this.Navigate.emit({
1092
+ Kind: 'email',
1093
+ EmailAddress: email
1094
+ });
1095
+ }
1096
+ /**
1097
+ * Handle click on a URL link in read-only mode.
1098
+ */
1099
+ OnUrlLinkClick(event) {
1100
+ event.preventDefault();
1101
+ event.stopPropagation();
1102
+ const url = this.FormatValue();
1103
+ if (!url)
1104
+ return;
1105
+ this.Navigate.emit({
1106
+ Kind: 'external-link',
1107
+ Url: url,
1108
+ OpenInNewTab: true
1109
+ });
1110
+ }
1111
+ /**
1112
+ * Handle value change from a native input.
1113
+ */
1114
+ OnInputChange(event) {
1115
+ const input = event.target;
1116
+ this.Value = input.value;
1117
+ }
1118
+ /**
1119
+ * Handle value change from a number input.
1120
+ */
1121
+ OnNumberChange(event) {
1122
+ const input = event.target;
1123
+ const num = input.valueAsNumber;
1124
+ this.Value = isNaN(num) ? null : num;
1125
+ }
1126
+ /**
1127
+ * Handle value change from a date input.
1128
+ */
1129
+ OnDateChange(event) {
1130
+ const input = event.target;
1131
+ this.Value = input.value ? new Date(input.value) : null;
1132
+ }
1133
+ /**
1134
+ * Handle value change from a checkbox.
1135
+ */
1136
+ OnCheckboxChange(event) {
1137
+ const input = event.target;
1138
+ this.Value = input.checked;
1139
+ }
1140
+ /**
1141
+ * Get date value formatted for HTML date input.
1142
+ */
1143
+ get DateInputValue() {
1144
+ const val = this.Value;
1145
+ if (!val)
1146
+ return '';
1147
+ const date = val instanceof Date ? val : new Date(String(val));
1148
+ if (isNaN(date.getTime()))
1149
+ return '';
1150
+ return date.toISOString().split('T')[0];
1151
+ }
1152
+ static ɵfac = function MjFormFieldComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MjFormFieldComponent)(); };
1153
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MjFormFieldComponent, selectors: [["mj-form-field"]], inputs: { Record: "Record", EditMode: "EditMode", FieldName: "FieldName", Type: "Type", LinkType: "LinkType", ShowLabel: "ShowLabel", FormContext: "FormContext", _deprecatedRecord: [0, "record", "_deprecatedRecord"], _deprecatedFormContext: [0, "formContext", "_deprecatedFormContext"], HideWhenEmptyInReadOnlyMode: "HideWhenEmptyInReadOnlyMode", DisplayNameOverride: "DisplayNameOverride", PossibleValuesOverride: "PossibleValuesOverride" }, outputs: { ValueChange: "ValueChange", Navigate: "Navigate" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 1, vars: 1, consts: [[1, "mj-forms-field", 3, "mj-forms-field--readonly", "mj-forms-field--dirty"], [1, "mj-forms-field", "mj-forms-field--editing", 3, "mj-forms-field--dirty", "mj-forms-field--required-empty", "mj-forms-field--has-error", "mj-forms-field--has-warning"], [1, "mj-forms-field"], [1, "mj-forms-field-label", 3, "innerHTML"], [1, "mj-forms-field-control"], [1, "mj-forms-field-value"], ["title", "View related record", 1, "mj-forms-field-link", "mj-forms-field-link--fk", 3, "click"], ["title", "Send email", 1, "mj-forms-field-link", 3, "click"], [1, "fa-solid", "fa-envelope", "mj-forms-field-link-icon"], ["title", "Open link", 1, "mj-forms-field-link", 3, "click"], [1, "fa-solid", "fa-arrow-up-right-from-square", "mj-forms-field-link-icon"], ["title", "View related record", 1, "mj-forms-field-link", 3, "click"], [1, "mj-forms-field-value", "mj-forms-field-value--long"], [1, "mj-forms-field-checkbox"], ["type", "checkbox", "disabled", "", 3, "checked"], [1, "mj-forms-field", "mj-forms-field--editing"], [1, "mj-fk-search", 3, "mj-fk-search--matched"], [1, "mj-forms-field-validation", "mj-forms-field-validation--error"], [1, "mj-forms-field-validation", "mj-forms-field-validation--warning"], [1, "mj-fk-search"], ["type", "text", "placeholder", "Search...", 1, "mj-forms-field-input", 3, "input", "focus", "blur", "value"], ["title", "Clear", 1, "mj-fk-clear"], [1, "fa-solid", "fa-magnifying-glass", "mj-fk-search-icon"], [1, "mj-fk-dropdown", 3, "ngStyle", "mj-fk-dropdown--above"], ["title", "Clear", 1, "mj-fk-clear", 3, "mousedown"], [1, "fa-solid", "fa-xmark"], [1, "mj-fk-dropdown", 3, "ngStyle"], [1, "mj-fk-option"], [1, "mj-fk-option", 3, "mousedown"], ["type", "text", 1, "mj-forms-field-input", 3, "value"], [1, "mj-forms-field-input", "mj-forms-field-input--textarea", 3, "value"], ["type", "number", 1, "mj-forms-field-input", "mj-forms-field-input--number", 3, "value"], ["type", "date", 1, "mj-forms-field-input", "mj-forms-field-input--date", 3, "value"], ["type", "text", 1, "mj-forms-field-input", 3, "change", "value"], [1, "mj-forms-field-input", "mj-forms-field-input--textarea", 3, "change", "value"], ["type", "number", 1, "mj-forms-field-input", "mj-forms-field-input--number", 3, "change", "value"], ["type", "date", 1, "mj-forms-field-input", "mj-forms-field-input--date", 3, "change", "value"], ["type", "checkbox", 3, "change", "checked"], ["tabindex", "0", 1, "mj-custom-select-trigger", 3, "click", "blur"], [1, "mj-custom-select-value"], [1, "fa-solid", "fa-chevron-down", "mj-custom-select-chevron"], [1, "mj-fk-option", 3, "mj-fk-option--selected"], ["type", "text", "placeholder", "Type to search...", 1, "mj-forms-field-input", 3, "input", "focus", "blur", "value"], [1, "mj-forms-field-validation-item"], [1, "fa-solid", "fa-circle-exclamation"], [1, "fa-solid", "fa-triangle-exclamation"]], template: function MjFormFieldComponent_Template(rf, ctx) { if (rf & 1) {
1154
+ i0.ɵɵconditionalCreate(0, MjFormFieldComponent_Conditional_0_Template, 2, 2);
1155
+ } if (rf & 2) {
1156
+ i0.ɵɵconditional(ctx.Record && (ctx.Record.IsSaved || !ctx.IsFieldReadOnly) ? 0 : -1);
1157
+ } }, dependencies: [i1.NgStyle], styles: ["\n\n\n\n[_nghost-%COMP%] {\n --mj-accent: #667eea;\n --mj-accent-light: rgba(102, 126, 234, 0.08);\n --mj-text-primary: #1f2937;\n --mj-text-secondary: #4b5563;\n --mj-text-muted: #9ca3af;\n --mj-border: #e5e7eb;\n --mj-error: #ef4444;\n --mj-warning: #f59e0b;\n --mj-card-bg: #ffffff;\n --mj-forms-field-label-width: 200px;\n --mj-forms-field-gap: 12px;\n --mj-transition: 200ms;\n\n display: contents;\n}\n\n.mj-forms-field[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: var(--mj-forms-field-label-width) 1fr;\n gap: var(--mj-forms-field-gap);\n align-items: baseline;\n padding: 8px 0;\n transition: all var(--mj-transition) ease;\n}\n\n\n\n.mj-forms-field--editing[_ngcontent-%COMP%]:hover {\n background: var(--mj-accent-light);\n border-radius: 6px;\n margin: 0 -8px;\n padding: 8px 8px;\n}\n\n\n\n\n\n.mj-forms-field-label[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n padding-top: 2px;\n word-break: break-word;\n}\n\n\n\n\n\n.mj-forms-field-control[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n}\n\n.mj-forms-field-control[_ngcontent-%COMP%] > *[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n\n\n\n\n.mj-forms-field--dirty[_ngcontent-%COMP%] .mj-forms-field-label[_ngcontent-%COMP%]::after {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--mj-warning);\n margin-left: 6px;\n vertical-align: middle;\n}\n\n\n\n\n\n.mj-forms-field--required-empty[_ngcontent-%COMP%] .mj-forms-field-input[_ngcontent-%COMP%] {\n border-bottom-color: var(--mj-error);\n}\n\n.mj-forms-field--required-empty[_ngcontent-%COMP%] .mj-forms-field-input[_ngcontent-%COMP%]:focus {\n background: #fef2f2;\n border-bottom-color: var(--mj-error);\n}\n\n.mj-forms-field--required-empty[_ngcontent-%COMP%] .mj-custom-select-trigger[_ngcontent-%COMP%] {\n border-bottom-color: var(--mj-error);\n}\n\n.mj-forms-field--required-empty[_ngcontent-%COMP%] .mj-fk-search[_ngcontent-%COMP%] .mj-forms-field-input[_ngcontent-%COMP%] {\n border-bottom-color: var(--mj-error);\n}\n\n\n\n\n\n.mj-forms-field-value[_ngcontent-%COMP%] {\n font-size: 14px;\n color: var(--mj-text-primary);\n min-height: 24px;\n display: flex;\n align-items: center;\n overflow-wrap: break-word;\n word-break: break-word;\n}\n\n.mj-forms-field-value--long[_ngcontent-%COMP%] {\n white-space: pre-line;\n max-height: 300px;\n overflow: auto;\n}\n\n\n\n.mj-forms-field--readonly[_ngcontent-%COMP%] {\n opacity: 0.7;\n}\n\n.mj-forms-field--readonly[_ngcontent-%COMP%] .mj-forms-field-value[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n}\n\n\n\n\n\n.mj-forms-field-link[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n color: var(--mj-accent);\n cursor: pointer;\n font-weight: 500;\n transition: color var(--mj-transition);\n text-decoration: none;\n}\n\n.mj-forms-field-link[_ngcontent-%COMP%]:hover {\n color: #5a6fd6;\n}\n\n.mj-forms-field-link-icon[_ngcontent-%COMP%] {\n font-size: 11px;\n opacity: 0;\n transition: opacity var(--mj-transition);\n}\n\n.mj-forms-field-link[_ngcontent-%COMP%]:hover .mj-forms-field-link-icon[_ngcontent-%COMP%] {\n opacity: 1;\n}\n\n\n\n.mj-forms-field-link--fk[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 400;\n}\n\n\n\n\n\n.mj-forms-field-input[_ngcontent-%COMP%] {\n font-size: 14px;\n font-family: inherit;\n color: var(--mj-text-primary);\n background: transparent;\n border: none;\n border-bottom: 1.5px solid var(--mj-border);\n padding: 4px 0;\n width: 100%;\n outline: none;\n transition: border-color var(--mj-transition) ease,\n background var(--mj-transition) ease;\n}\n\n.mj-forms-field-input[_ngcontent-%COMP%]:focus {\n border-bottom-color: var(--mj-accent);\n background: var(--mj-accent-light);\n border-radius: 4px 4px 0 0;\n padding: 4px 6px;\n}\n\n.mj-forms-field-input[_ngcontent-%COMP%]::placeholder {\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n\n\n.mj-forms-field-input--textarea[_ngcontent-%COMP%] {\n resize: vertical;\n min-height: 60px;\n border: 1.5px solid var(--mj-border);\n border-radius: 6px;\n padding: 8px 10px;\n background: white;\n font-family: inherit;\n}\n\n.mj-forms-field-input--textarea[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-accent);\n box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);\n background: white;\n padding: 8px 10px;\n border-radius: 6px;\n}\n\n\n\n.mj-forms-field-input--number[_ngcontent-%COMP%] {\n max-width: 200px;\n}\n\n\n\n.mj-forms-field-input--date[_ngcontent-%COMP%] {\n max-width: 220px;\n}\n\n\n\n\n\n.mj-forms-field-checkbox[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.mj-forms-field-checkbox[_ngcontent-%COMP%] input[type=\"checkbox\"][_ngcontent-%COMP%] {\n appearance: none;\n width: 18px;\n height: 18px;\n border: 2px solid var(--mj-border);\n border-radius: 4px;\n cursor: pointer;\n position: relative;\n transition: all var(--mj-transition) ease;\n flex-shrink: 0;\n}\n\n.mj-forms-field-checkbox[_ngcontent-%COMP%] input[type=\"checkbox\"][_ngcontent-%COMP%]:checked {\n background: var(--mj-accent);\n border-color: var(--mj-accent);\n}\n\n.mj-forms-field-checkbox[_ngcontent-%COMP%] input[type=\"checkbox\"][_ngcontent-%COMP%]:checked::after {\n content: '';\n position: absolute;\n left: 5px;\n top: 1px;\n width: 5px;\n height: 9px;\n border: solid white;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n}\n\n.mj-forms-field-checkbox[_ngcontent-%COMP%] input[type=\"checkbox\"][_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: default;\n}\n\n\n\n\n\n.mj-custom-select-trigger[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 14px;\n font-family: inherit;\n color: var(--mj-text-primary);\n background: transparent;\n border: none;\n border-bottom: 1.5px solid var(--mj-border);\n padding: 4px 0;\n width: 100%;\n cursor: pointer;\n outline: none;\n transition: border-color var(--mj-transition) ease,\n background var(--mj-transition) ease;\n}\n\n.mj-custom-select-trigger[_ngcontent-%COMP%]:focus {\n border-bottom-color: var(--mj-accent);\n background: var(--mj-accent-light);\n border-radius: 4px 4px 0 0;\n padding: 4px 6px;\n}\n\n.mj-custom-select-value[_ngcontent-%COMP%] {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mj-custom-select-value--placeholder[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n.mj-custom-select-chevron[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n transition: transform var(--mj-transition) ease;\n flex-shrink: 0;\n margin-left: 8px;\n}\n\n.mj-custom-select-chevron--open[_ngcontent-%COMP%] {\n transform: rotate(180deg);\n}\n\n\n\n\n\n.mj-fk-search[_ngcontent-%COMP%] {\n position: relative;\n display: flex;\n align-items: center;\n width: 100%;\n}\n\n.mj-fk-search[_ngcontent-%COMP%] .mj-forms-field-input[_ngcontent-%COMP%] {\n padding-right: 28px;\n}\n\n.mj-fk-search-icon[_ngcontent-%COMP%] {\n position: absolute;\n right: 4px;\n color: var(--mj-text-muted);\n font-size: 12px;\n pointer-events: none;\n}\n\n.mj-fk-clear[_ngcontent-%COMP%] {\n position: absolute;\n right: 2px;\n background: none;\n border: none;\n color: var(--mj-text-muted);\n cursor: pointer;\n font-size: 11px;\n padding: 4px 6px;\n line-height: 1;\n border-radius: 3px;\n transition: all var(--mj-transition);\n}\n\n.mj-fk-clear[_ngcontent-%COMP%]:hover {\n color: var(--mj-error);\n background: rgba(239, 68, 68, 0.08);\n}\n\n\n\n.mj-fk-search--matched[_ngcontent-%COMP%] .mj-forms-field-input[_ngcontent-%COMP%] {\n border-bottom: 2px solid var(--mj-accent);\n}\n\n\n\n\n\n.mj-fk-dropdown[_ngcontent-%COMP%] {\n background: var(--mj-card-bg);\n border: 1px solid var(--mj-border);\n border-radius: 8px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);\n max-height: 240px;\n overflow-y: auto;\n padding: 4px;\n animation: _ngcontent-%COMP%_mj-dropdown-in 150ms ease;\n}\n\n.mj-fk-dropdown--above[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_mj-dropdown-in-above 150ms ease;\n}\n\n.mj-fk-option[_ngcontent-%COMP%] {\n padding: 10px 14px;\n font-size: 14px;\n color: var(--mj-text-primary);\n cursor: pointer;\n transition: background var(--mj-transition);\n border-radius: 6px;\n}\n\n.mj-fk-option[_ngcontent-%COMP%]:hover {\n background: var(--mj-accent-light);\n}\n\n.mj-fk-option--selected[_ngcontent-%COMP%] {\n background: var(--mj-accent-light);\n font-weight: 600;\n color: var(--mj-accent);\n}\n\n.mj-fk-option--selected[_ngcontent-%COMP%]:hover {\n background: rgba(102, 126, 234, 0.15);\n}\n\n@keyframes _ngcontent-%COMP%_mj-dropdown-in {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n@keyframes _ngcontent-%COMP%_mj-dropdown-in-above {\n from { opacity: 0; transform: translateY(4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n\n\n\n\n.mj-forms-field-label[_ngcontent-%COMP%] [_ngcontent-%COMP%]:global(.mj-forms-search-highlight) {\n background-color: #fef08a;\n color: #854d0e;\n padding: 0;\n border-radius: 2px;\n font-weight: 700;\n box-shadow: 0 0 0 2px #fef08a;\n}\n\n\n\n\n\n.mj-forms-field--has-error[_ngcontent-%COMP%] .mj-forms-field-input[_ngcontent-%COMP%] {\n border-bottom-color: var(--mj-error);\n border-bottom-width: 2px;\n}\n\n.mj-forms-field--has-error[_ngcontent-%COMP%] .mj-forms-field-input[_ngcontent-%COMP%]:focus {\n background: #fef2f2;\n border-bottom-color: var(--mj-error);\n}\n\n.mj-forms-field--has-error[_ngcontent-%COMP%] .mj-forms-field-input--textarea[_ngcontent-%COMP%] {\n border-color: var(--mj-error);\n}\n\n.mj-forms-field--has-error[_ngcontent-%COMP%] .mj-forms-field-input--textarea[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-error);\n box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.08);\n}\n\n.mj-forms-field--has-error[_ngcontent-%COMP%] .mj-custom-select-trigger[_ngcontent-%COMP%] {\n border-bottom-color: var(--mj-error);\n border-bottom-width: 2px;\n}\n\n.mj-forms-field--has-error[_ngcontent-%COMP%] .mj-fk-search[_ngcontent-%COMP%] .mj-forms-field-input[_ngcontent-%COMP%] {\n border-bottom-color: var(--mj-error);\n border-bottom-width: 2px;\n}\n\n\n\n\n\n.mj-forms-field--has-warning[_ngcontent-%COMP%] .mj-forms-field-input[_ngcontent-%COMP%] {\n border-bottom-color: var(--mj-warning);\n border-bottom-width: 2px;\n}\n\n.mj-forms-field--has-warning[_ngcontent-%COMP%] .mj-forms-field-input[_ngcontent-%COMP%]:focus {\n background: #fffbeb;\n border-bottom-color: var(--mj-warning);\n}\n\n.mj-forms-field--has-warning[_ngcontent-%COMP%] .mj-forms-field-input--textarea[_ngcontent-%COMP%] {\n border-color: var(--mj-warning);\n}\n\n.mj-forms-field--has-warning[_ngcontent-%COMP%] .mj-forms-field-input--textarea[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-warning);\n box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.08);\n}\n\n.mj-forms-field--has-warning[_ngcontent-%COMP%] .mj-custom-select-trigger[_ngcontent-%COMP%] {\n border-bottom-color: var(--mj-warning);\n border-bottom-width: 2px;\n}\n\n.mj-forms-field--has-warning[_ngcontent-%COMP%] .mj-fk-search[_ngcontent-%COMP%] .mj-forms-field-input[_ngcontent-%COMP%] {\n border-bottom-color: var(--mj-warning);\n border-bottom-width: 2px;\n}\n\n\n\n\n\n.mj-forms-field-validation[_ngcontent-%COMP%] {\n grid-column: 2;\n display: flex;\n flex-direction: column;\n gap: 2px;\n margin-top: -2px;\n overflow: hidden;\n animation: _ngcontent-%COMP%_mj-validation-slide-in 200ms ease-out;\n}\n\n.mj-forms-field-validation-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n font-size: 12px;\n line-height: 1.4;\n padding: 3px 0 1px;\n}\n\n.mj-forms-field-validation-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 11px;\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n\n\n.mj-forms-field-validation--error[_ngcontent-%COMP%] .mj-forms-field-validation-item[_ngcontent-%COMP%] {\n color: var(--mj-error);\n}\n\n.mj-forms-field-validation--error[_ngcontent-%COMP%] .mj-forms-field-validation-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-error);\n}\n\n\n\n.mj-forms-field-validation--warning[_ngcontent-%COMP%] .mj-forms-field-validation-item[_ngcontent-%COMP%] {\n color: #b45309;\n}\n\n.mj-forms-field-validation--warning[_ngcontent-%COMP%] .mj-forms-field-validation-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-warning);\n}\n\n@keyframes _ngcontent-%COMP%_mj-validation-slide-in {\n from {\n opacity: 0;\n max-height: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n max-height: 100px;\n transform: translateY(0);\n }\n}\n\n\n\n\n\n@media (max-width: 640px) {\n .mj-forms-field[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n gap: 4px;\n }\n\n .mj-forms-field-label[_ngcontent-%COMP%] {\n font-size: 12px;\n }\n\n .mj-forms-field-validation[_ngcontent-%COMP%] {\n grid-column: 1;\n }\n}"], changeDetection: 0 });
1158
+ }
1159
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MjFormFieldComponent, [{
1160
+ type: Component,
1161
+ args: [{ standalone: false, selector: 'mj-form-field', changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (Record && (Record.IsSaved || !IsFieldReadOnly)) {\n <!-- READ-ONLY MODE -->\n @if ((!EditMode || IsFieldReadOnly) && !ShouldHideField) {\n <div class=\"mj-forms-field\" [class.mj-forms-field--readonly]=\"EditMode && IsFieldReadOnly\" [class.mj-forms-field--dirty]=\"IsDirty\">\n @if (ShowLabel) {\n <label class=\"mj-forms-field-label\" [innerHTML]=\"HighlightedDisplayName\"></label>\n }\n <div class=\"mj-forms-field-control\">\n <!-- FK fields with a mapped name field: show the name as a hyperlink -->\n @if (IsFKWithNameField) {\n <span class=\"mj-forms-field-value\">\n <a class=\"mj-forms-field-link mj-forms-field-link--fk\" (click)=\"OnRecordLinkClick($event)\" title=\"View related record\">\n {{ FKDisplayName }}\n </a>\n </span>\n }\n @else {\n @switch (LinkType) {\n @case ('Email') {\n <span class=\"mj-forms-field-value\">\n <a class=\"mj-forms-field-link\" (click)=\"OnEmailLinkClick($event)\" title=\"Send email\">\n {{ FormatValue() }}\n <i class=\"fa-solid fa-envelope mj-forms-field-link-icon\"></i>\n </a>\n </span>\n }\n @case ('URL') {\n <span class=\"mj-forms-field-value\">\n <a class=\"mj-forms-field-link\" (click)=\"OnUrlLinkClick($event)\" title=\"Open link\">\n {{ FormatValue() }}\n <i class=\"fa-solid fa-arrow-up-right-from-square mj-forms-field-link-icon\"></i>\n </a>\n </span>\n }\n @case ('Record') {\n <span class=\"mj-forms-field-value\">\n <a class=\"mj-forms-field-link\" (click)=\"OnRecordLinkClick($event)\" title=\"View related record\">\n {{ FormatValue() }}\n <i class=\"fa-solid fa-arrow-up-right-from-square mj-forms-field-link-icon\"></i>\n </a>\n </span>\n }\n @default {\n @if (Type === 'checkbox') {\n <span class=\"mj-forms-field-value\">\n <div class=\"mj-forms-field-checkbox\">\n <input type=\"checkbox\" [checked]=\"!!Value\" disabled>\n </div>\n </span>\n }\n @else if (IsLongText) {\n <span class=\"mj-forms-field-value mj-forms-field-value--long\">{{ FormatValue() }}</span>\n }\n @else {\n <span class=\"mj-forms-field-value\">{{ FormatValue() }}</span>\n }\n }\n }\n }\n </div>\n </div>\n }\n\n <!-- EDIT MODE -->\n @if (EditMode && !IsFieldReadOnly) {\n <div class=\"mj-forms-field mj-forms-field--editing\" [class.mj-forms-field--dirty]=\"IsDirty\" [class.mj-forms-field--required-empty]=\"IsRequiredEmpty\" [class.mj-forms-field--has-error]=\"ShowErrors\" [class.mj-forms-field--has-warning]=\"ShowWarnings\">\n @if (ShowLabel) {\n <label class=\"mj-forms-field-label\" [innerHTML]=\"HighlightedDisplayName\"></label>\n }\n <div class=\"mj-forms-field-control\">\n <!-- Foreign Key fields get custom search autocomplete -->\n @if (HasRelatedEntity) {\n <div class=\"mj-fk-search\" [class.mj-fk-search--matched]=\"FKIsMatched\">\n <input type=\"text\"\n class=\"mj-forms-field-input\"\n [value]=\"FKSearchText\"\n (input)=\"OnFKInput($event)\"\n (focus)=\"OnFKFocus($event)\"\n (blur)=\"OnFKBlur()\"\n placeholder=\"Search...\">\n @if (FKSearchText) {\n <button class=\"mj-fk-clear\" (mousedown)=\"OnFKClearClick($event)\" title=\"Clear\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n } @else {\n <i class=\"fa-solid fa-magnifying-glass mj-fk-search-icon\"></i>\n }\n @if (ShowFKDropdown && FKSuggestions.length > 0) {\n <div class=\"mj-fk-dropdown\" [ngStyle]=\"DropdownPositionStyle\" [class.mj-fk-dropdown--above]=\"OpenAbove\">\n @for (suggestion of FKSuggestions; track suggestion.PrimaryKeyValue) {\n <div class=\"mj-fk-option\" (mousedown)=\"OnFKSelectItem(suggestion, $event)\">\n {{ suggestion.DisplayName }}\n </div>\n }\n </div>\n }\n </div>\n }\n @else {\n @switch (Type) {\n @case ('textbox') {\n <input type=\"text\"\n class=\"mj-forms-field-input\"\n [value]=\"FormatValue()\"\n (change)=\"OnInputChange($event)\">\n }\n @case ('textarea') {\n <textarea\n class=\"mj-forms-field-input mj-forms-field-input--textarea\"\n [value]=\"FormatValue()\"\n (change)=\"OnInputChange($event)\"></textarea>\n }\n @case ('number') {\n <input type=\"number\"\n class=\"mj-forms-field-input mj-forms-field-input--number\"\n [value]=\"Value\"\n (change)=\"OnNumberChange($event)\">\n }\n @case ('datepicker') {\n <input type=\"date\"\n class=\"mj-forms-field-input mj-forms-field-input--date\"\n [value]=\"DateInputValue\"\n (change)=\"OnDateChange($event)\">\n }\n @case ('checkbox') {\n <div class=\"mj-forms-field-checkbox\">\n <input type=\"checkbox\"\n [checked]=\"!!Value\"\n (change)=\"OnCheckboxChange($event)\">\n </div>\n }\n @case ('select') {\n <div class=\"mj-custom-select-trigger\"\n tabindex=\"0\"\n (click)=\"OnSelectTriggerClick($event)\"\n (blur)=\"OnSelectTriggerBlur()\">\n <span class=\"mj-custom-select-value\" [class.mj-custom-select-value--placeholder]=\"!FormatValue()\">\n {{ FormatValue() || '-- Select --' }}\n </span>\n <i class=\"fa-solid fa-chevron-down mj-custom-select-chevron\" [class.mj-custom-select-chevron--open]=\"ShowSelectDropdown\"></i>\n </div>\n @if (ShowSelectDropdown && PossibleValues.length > 0) {\n <div class=\"mj-fk-dropdown\" [ngStyle]=\"DropdownPositionStyle\" [class.mj-fk-dropdown--above]=\"OpenAbove\">\n @for (option of PossibleValues; track option) {\n <div class=\"mj-fk-option\" [class.mj-fk-option--selected]=\"FormatValue() === option\" (mousedown)=\"OnSelectOptionClick(option, $event)\">\n {{ option }}\n </div>\n }\n </div>\n }\n }\n @case ('autocomplete') {\n <div class=\"mj-fk-search\">\n <input type=\"text\"\n class=\"mj-forms-field-input\"\n [value]=\"FormatValue()\"\n (input)=\"OnValueListInput($event)\"\n (focus)=\"OnValueListFocus($event)\"\n (blur)=\"OnValueListBlur()\"\n placeholder=\"Type to search...\">\n <i class=\"fa-solid fa-magnifying-glass mj-fk-search-icon\"></i>\n @if (ShowValueListDropdown && FilteredPossibleValues.length > 0) {\n <div class=\"mj-fk-dropdown\" [ngStyle]=\"DropdownPositionStyle\" [class.mj-fk-dropdown--above]=\"OpenAbove\">\n @for (option of FilteredPossibleValues; track option) {\n <div class=\"mj-fk-option\" (mousedown)=\"OnValueListSelect(option, $event)\">\n {{ option }}\n </div>\n }\n </div>\n }\n </div>\n }\n @default {\n <input type=\"text\"\n class=\"mj-forms-field-input\"\n [value]=\"FormatValue()\"\n (change)=\"OnInputChange($event)\">\n }\n }\n }\n </div>\n <!-- Inline validation messages -->\n @if (DisplayErrors.length > 0) {\n <div class=\"mj-forms-field-validation mj-forms-field-validation--error\">\n @for (error of DisplayErrors; track error.Message) {\n <div class=\"mj-forms-field-validation-item\">\n <i class=\"fa-solid fa-circle-exclamation\"></i>\n <span>{{ error.Message }}</span>\n </div>\n }\n </div>\n }\n @if (DisplayWarnings.length > 0) {\n <div class=\"mj-forms-field-validation mj-forms-field-validation--warning\">\n @for (warning of DisplayWarnings; track warning.Message) {\n <div class=\"mj-forms-field-validation-item\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <span>{{ warning.Message }}</span>\n </div>\n }\n </div>\n }\n </div>\n }\n}\n", styles: ["/* ============================================\n FORM FIELD - Shared styles for read and edit\n ============================================ */\n:host {\n --mj-accent: #667eea;\n --mj-accent-light: rgba(102, 126, 234, 0.08);\n --mj-text-primary: #1f2937;\n --mj-text-secondary: #4b5563;\n --mj-text-muted: #9ca3af;\n --mj-border: #e5e7eb;\n --mj-error: #ef4444;\n --mj-warning: #f59e0b;\n --mj-card-bg: #ffffff;\n --mj-forms-field-label-width: 200px;\n --mj-forms-field-gap: 12px;\n --mj-transition: 200ms;\n\n display: contents;\n}\n\n.mj-forms-field {\n display: grid;\n grid-template-columns: var(--mj-forms-field-label-width) 1fr;\n gap: var(--mj-forms-field-gap);\n align-items: baseline;\n padding: 8px 0;\n transition: all var(--mj-transition) ease;\n}\n\n/* Subtle row hover in edit mode */\n.mj-forms-field--editing:hover {\n background: var(--mj-accent-light);\n border-radius: 6px;\n margin: 0 -8px;\n padding: 8px 8px;\n}\n\n/* ============================================\n LABEL\n ============================================ */\n.mj-forms-field-label {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n padding-top: 2px;\n word-break: break-word;\n}\n\n/* ============================================\n FIELD CONTROL WRAPPER (value + dirty dot)\n ============================================ */\n.mj-forms-field-control {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n}\n\n.mj-forms-field-control > * {\n flex: 1;\n min-width: 0;\n}\n\n/* ============================================\n DIRTY INDICATOR (solid amber dot after label)\n ============================================ */\n.mj-forms-field--dirty .mj-forms-field-label::after {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--mj-warning);\n margin-left: 6px;\n vertical-align: middle;\n}\n\n/* ============================================\n REQUIRED FIELD - red underline when empty\n ============================================ */\n.mj-forms-field--required-empty .mj-forms-field-input {\n border-bottom-color: var(--mj-error);\n}\n\n.mj-forms-field--required-empty .mj-forms-field-input:focus {\n background: #fef2f2;\n border-bottom-color: var(--mj-error);\n}\n\n.mj-forms-field--required-empty .mj-custom-select-trigger {\n border-bottom-color: var(--mj-error);\n}\n\n.mj-forms-field--required-empty .mj-fk-search .mj-forms-field-input {\n border-bottom-color: var(--mj-error);\n}\n\n/* ============================================\n READ-ONLY VALUE\n ============================================ */\n.mj-forms-field-value {\n font-size: 14px;\n color: var(--mj-text-primary);\n min-height: 24px;\n display: flex;\n align-items: center;\n overflow-wrap: break-word;\n word-break: break-word;\n}\n\n.mj-forms-field-value--long {\n white-space: pre-line;\n max-height: 300px;\n overflow: auto;\n}\n\n/* Read-only fields in edit mode */\n.mj-forms-field--readonly {\n opacity: 0.7;\n}\n\n.mj-forms-field--readonly .mj-forms-field-value {\n color: var(--mj-text-secondary);\n}\n\n/* ============================================\n LINKS (FK, email, URL)\n ============================================ */\n.mj-forms-field-link {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n color: var(--mj-accent);\n cursor: pointer;\n font-weight: 500;\n transition: color var(--mj-transition);\n text-decoration: none;\n}\n\n.mj-forms-field-link:hover {\n color: #5a6fd6;\n}\n\n.mj-forms-field-link-icon {\n font-size: 11px;\n opacity: 0;\n transition: opacity var(--mj-transition);\n}\n\n.mj-forms-field-link:hover .mj-forms-field-link-icon {\n opacity: 1;\n}\n\n/* FK fields showing entity name instead of raw ID */\n.mj-forms-field-link--fk {\n font-size: 14px;\n font-weight: 400;\n}\n\n/* ============================================\n EDIT MODE INPUTS - Clean underline style\n ============================================ */\n.mj-forms-field-input {\n font-size: 14px;\n font-family: inherit;\n color: var(--mj-text-primary);\n background: transparent;\n border: none;\n border-bottom: 1.5px solid var(--mj-border);\n padding: 4px 0;\n width: 100%;\n outline: none;\n transition: border-color var(--mj-transition) ease,\n background var(--mj-transition) ease;\n}\n\n.mj-forms-field-input:focus {\n border-bottom-color: var(--mj-accent);\n background: var(--mj-accent-light);\n border-radius: 4px 4px 0 0;\n padding: 4px 6px;\n}\n\n.mj-forms-field-input::placeholder {\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n/* Textarea variant */\n.mj-forms-field-input--textarea {\n resize: vertical;\n min-height: 60px;\n border: 1.5px solid var(--mj-border);\n border-radius: 6px;\n padding: 8px 10px;\n background: white;\n font-family: inherit;\n}\n\n.mj-forms-field-input--textarea:focus {\n border-color: var(--mj-accent);\n box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);\n background: white;\n padding: 8px 10px;\n border-radius: 6px;\n}\n\n/* Number input - narrower */\n.mj-forms-field-input--number {\n max-width: 200px;\n}\n\n/* Date input - narrower */\n.mj-forms-field-input--date {\n max-width: 220px;\n}\n\n/* ============================================\n CHECKBOX (custom styled)\n ============================================ */\n.mj-forms-field-checkbox {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.mj-forms-field-checkbox input[type=\"checkbox\"] {\n appearance: none;\n width: 18px;\n height: 18px;\n border: 2px solid var(--mj-border);\n border-radius: 4px;\n cursor: pointer;\n position: relative;\n transition: all var(--mj-transition) ease;\n flex-shrink: 0;\n}\n\n.mj-forms-field-checkbox input[type=\"checkbox\"]:checked {\n background: var(--mj-accent);\n border-color: var(--mj-accent);\n}\n\n.mj-forms-field-checkbox input[type=\"checkbox\"]:checked::after {\n content: '';\n position: absolute;\n left: 5px;\n top: 1px;\n width: 5px;\n height: 9px;\n border: solid white;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n}\n\n.mj-forms-field-checkbox input[type=\"checkbox\"]:disabled {\n opacity: 0.5;\n cursor: default;\n}\n\n/* ============================================\n CUSTOM SELECT DROPDOWN (replaces native <select>)\n ============================================ */\n.mj-custom-select-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 14px;\n font-family: inherit;\n color: var(--mj-text-primary);\n background: transparent;\n border: none;\n border-bottom: 1.5px solid var(--mj-border);\n padding: 4px 0;\n width: 100%;\n cursor: pointer;\n outline: none;\n transition: border-color var(--mj-transition) ease,\n background var(--mj-transition) ease;\n}\n\n.mj-custom-select-trigger:focus {\n border-bottom-color: var(--mj-accent);\n background: var(--mj-accent-light);\n border-radius: 4px 4px 0 0;\n padding: 4px 6px;\n}\n\n.mj-custom-select-value {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mj-custom-select-value--placeholder {\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n.mj-custom-select-chevron {\n font-size: 11px;\n color: var(--mj-text-muted);\n transition: transform var(--mj-transition) ease;\n flex-shrink: 0;\n margin-left: 8px;\n}\n\n.mj-custom-select-chevron--open {\n transform: rotate(180deg);\n}\n\n/* ============================================\n FK SEARCH (custom autocomplete)\n ============================================ */\n.mj-fk-search {\n position: relative;\n display: flex;\n align-items: center;\n width: 100%;\n}\n\n.mj-fk-search .mj-forms-field-input {\n padding-right: 28px;\n}\n\n.mj-fk-search-icon {\n position: absolute;\n right: 4px;\n color: var(--mj-text-muted);\n font-size: 12px;\n pointer-events: none;\n}\n\n.mj-fk-clear {\n position: absolute;\n right: 2px;\n background: none;\n border: none;\n color: var(--mj-text-muted);\n cursor: pointer;\n font-size: 11px;\n padding: 4px 6px;\n line-height: 1;\n border-radius: 3px;\n transition: all var(--mj-transition);\n}\n\n.mj-fk-clear:hover {\n color: var(--mj-error);\n background: rgba(239, 68, 68, 0.08);\n}\n\n/* Matched state: solid accent underline */\n.mj-fk-search--matched .mj-forms-field-input {\n border-bottom: 2px solid var(--mj-accent);\n}\n\n/* ============================================\n FLOATING DROPDOWN PANEL (position: fixed via ngStyle)\n ============================================ */\n.mj-fk-dropdown {\n background: var(--mj-card-bg);\n border: 1px solid var(--mj-border);\n border-radius: 8px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);\n max-height: 240px;\n overflow-y: auto;\n padding: 4px;\n animation: mj-dropdown-in 150ms ease;\n}\n\n.mj-fk-dropdown--above {\n animation: mj-dropdown-in-above 150ms ease;\n}\n\n.mj-fk-option {\n padding: 10px 14px;\n font-size: 14px;\n color: var(--mj-text-primary);\n cursor: pointer;\n transition: background var(--mj-transition);\n border-radius: 6px;\n}\n\n.mj-fk-option:hover {\n background: var(--mj-accent-light);\n}\n\n.mj-fk-option--selected {\n background: var(--mj-accent-light);\n font-weight: 600;\n color: var(--mj-accent);\n}\n\n.mj-fk-option--selected:hover {\n background: rgba(102, 126, 234, 0.15);\n}\n\n@keyframes mj-dropdown-in {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n@keyframes mj-dropdown-in-above {\n from { opacity: 0; transform: translateY(4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n/* ============================================\n SEARCH HIGHLIGHTING\n ============================================ */\n.mj-forms-field-label :global(.mj-forms-search-highlight) {\n background-color: #fef08a;\n color: #854d0e;\n padding: 0;\n border-radius: 2px;\n font-weight: 700;\n box-shadow: 0 0 0 2px #fef08a;\n}\n\n/* ============================================\n VALIDATION ERROR STATE\n ============================================ */\n.mj-forms-field--has-error .mj-forms-field-input {\n border-bottom-color: var(--mj-error);\n border-bottom-width: 2px;\n}\n\n.mj-forms-field--has-error .mj-forms-field-input:focus {\n background: #fef2f2;\n border-bottom-color: var(--mj-error);\n}\n\n.mj-forms-field--has-error .mj-forms-field-input--textarea {\n border-color: var(--mj-error);\n}\n\n.mj-forms-field--has-error .mj-forms-field-input--textarea:focus {\n border-color: var(--mj-error);\n box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.08);\n}\n\n.mj-forms-field--has-error .mj-custom-select-trigger {\n border-bottom-color: var(--mj-error);\n border-bottom-width: 2px;\n}\n\n.mj-forms-field--has-error .mj-fk-search .mj-forms-field-input {\n border-bottom-color: var(--mj-error);\n border-bottom-width: 2px;\n}\n\n/* ============================================\n VALIDATION WARNING STATE\n ============================================ */\n.mj-forms-field--has-warning .mj-forms-field-input {\n border-bottom-color: var(--mj-warning);\n border-bottom-width: 2px;\n}\n\n.mj-forms-field--has-warning .mj-forms-field-input:focus {\n background: #fffbeb;\n border-bottom-color: var(--mj-warning);\n}\n\n.mj-forms-field--has-warning .mj-forms-field-input--textarea {\n border-color: var(--mj-warning);\n}\n\n.mj-forms-field--has-warning .mj-forms-field-input--textarea:focus {\n border-color: var(--mj-warning);\n box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.08);\n}\n\n.mj-forms-field--has-warning .mj-custom-select-trigger {\n border-bottom-color: var(--mj-warning);\n border-bottom-width: 2px;\n}\n\n.mj-forms-field--has-warning .mj-fk-search .mj-forms-field-input {\n border-bottom-color: var(--mj-warning);\n border-bottom-width: 2px;\n}\n\n/* ============================================\n VALIDATION MESSAGE AREA\n ============================================ */\n.mj-forms-field-validation {\n grid-column: 2;\n display: flex;\n flex-direction: column;\n gap: 2px;\n margin-top: -2px;\n overflow: hidden;\n animation: mj-validation-slide-in 200ms ease-out;\n}\n\n.mj-forms-field-validation-item {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n font-size: 12px;\n line-height: 1.4;\n padding: 3px 0 1px;\n}\n\n.mj-forms-field-validation-item i {\n font-size: 11px;\n margin-top: 2px;\n flex-shrink: 0;\n}\n\n/* Error message colors */\n.mj-forms-field-validation--error .mj-forms-field-validation-item {\n color: var(--mj-error);\n}\n\n.mj-forms-field-validation--error .mj-forms-field-validation-item i {\n color: var(--mj-error);\n}\n\n/* Warning message colors */\n.mj-forms-field-validation--warning .mj-forms-field-validation-item {\n color: #b45309;\n}\n\n.mj-forms-field-validation--warning .mj-forms-field-validation-item i {\n color: var(--mj-warning);\n}\n\n@keyframes mj-validation-slide-in {\n from {\n opacity: 0;\n max-height: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n max-height: 100px;\n transform: translateY(0);\n }\n}\n\n/* ============================================\n RESPONSIVE\n ============================================ */\n@media (max-width: 640px) {\n .mj-forms-field {\n grid-template-columns: 1fr;\n gap: 4px;\n }\n\n .mj-forms-field-label {\n font-size: 12px;\n }\n\n .mj-forms-field-validation {\n grid-column: 1;\n }\n}\n"] }]
1162
+ }], null, { Record: [{
1163
+ type: Input
1164
+ }], EditMode: [{
1165
+ type: Input
1166
+ }], FieldName: [{
1167
+ type: Input
1168
+ }], Type: [{
1169
+ type: Input
1170
+ }], LinkType: [{
1171
+ type: Input
1172
+ }], ShowLabel: [{
1173
+ type: Input
1174
+ }], FormContext: [{
1175
+ type: Input
1176
+ }], _deprecatedRecord: [{
1177
+ type: Input,
1178
+ args: ['record']
1179
+ }], _deprecatedFormContext: [{
1180
+ type: Input,
1181
+ args: ['formContext']
1182
+ }], HideWhenEmptyInReadOnlyMode: [{
1183
+ type: Input
1184
+ }], DisplayNameOverride: [{
1185
+ type: Input
1186
+ }], PossibleValuesOverride: [{
1187
+ type: Input
1188
+ }], ValueChange: [{
1189
+ type: Output
1190
+ }], Navigate: [{
1191
+ type: Output
1192
+ }] }); })();
1193
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MjFormFieldComponent, { className: "MjFormFieldComponent", filePath: "src/lib/field/form-field.component.ts", lineNumber: 49 }); })();
1194
+ //# sourceMappingURL=form-field.component.js.map