@memberjunction/ng-search 5.33.0 → 5.34.1

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 (34) hide show
  1. package/dist/__tests__/search.service.test.js +101 -0
  2. package/dist/__tests__/search.service.test.js.map +1 -1
  3. package/dist/lib/module.d.ts +6 -4
  4. package/dist/lib/module.d.ts.map +1 -1
  5. package/dist/lib/module.js +14 -4
  6. package/dist/lib/module.js.map +1 -1
  7. package/dist/lib/search-composite.component.d.ts +29 -2
  8. package/dist/lib/search-composite.component.d.ts.map +1 -1
  9. package/dist/lib/search-composite.component.js +72 -7
  10. package/dist/lib/search-composite.component.js.map +1 -1
  11. package/dist/lib/search-overlay.component.d.ts +42 -1
  12. package/dist/lib/search-overlay.component.d.ts.map +1 -1
  13. package/dist/lib/search-overlay.component.js +301 -128
  14. package/dist/lib/search-overlay.component.js.map +1 -1
  15. package/dist/lib/search-scope-child-grid.component.d.ts +166 -0
  16. package/dist/lib/search-scope-child-grid.component.d.ts.map +1 -0
  17. package/dist/lib/search-scope-child-grid.component.js +575 -0
  18. package/dist/lib/search-scope-child-grid.component.js.map +1 -0
  19. package/dist/lib/search-scope-selector.component.d.ts +71 -0
  20. package/dist/lib/search-scope-selector.component.d.ts.map +1 -0
  21. package/dist/lib/search-scope-selector.component.js +354 -0
  22. package/dist/lib/search-scope-selector.component.js.map +1 -0
  23. package/dist/lib/search-types.d.ts +18 -0
  24. package/dist/lib/search-types.d.ts.map +1 -1
  25. package/dist/lib/search-types.js.map +1 -1
  26. package/dist/lib/search.service.d.ts +36 -2
  27. package/dist/lib/search.service.d.ts.map +1 -1
  28. package/dist/lib/search.service.js +81 -2
  29. package/dist/lib/search.service.js.map +1 -1
  30. package/dist/public-api.d.ts +2 -0
  31. package/dist/public-api.d.ts.map +1 -1
  32. package/dist/public-api.js +2 -0
  33. package/dist/public-api.js.map +1 -1
  34. package/package.json +7 -7
@@ -0,0 +1,575 @@
1
+ /**
2
+ * @fileoverview Generic Search Scope Child Grid
3
+ *
4
+ * A parameter-driven grid for editing the child-collection tabs of a Search Scope:
5
+ * - Providers (`MJ: Search Scope Providers`)
6
+ * - External Indexes (`MJ: Search Scope External Indexes`)
7
+ * - Entities (`MJ: Search Scope Entities`)
8
+ * - Storage Accounts (`MJ: Search Scope Storage Accounts`)
9
+ *
10
+ * Also reused on the AI Agent form for assigning scopes to an agent (the
11
+ * `MJ: AI Agent Search Scopes` join table). The `ParentID` + `ParentFieldName`
12
+ * inputs keep it generic — parent can be a SearchScope OR an AI Agent.
13
+ *
14
+ * One component, many configurations. The parent passes:
15
+ * - `ParentID` — the parent record ID (SearchScope ID, AI Agent ID, etc.)
16
+ * - `ChildEntityName` — the MJ entity name of the child table
17
+ * - `ParentFieldName` — the FK column on the child that references the parent
18
+ * - `Columns` — declarative column spec (type, label, optional lookup source)
19
+ *
20
+ * This is a **Generic component** (see `packages/Angular/Generic/CLAUDE.md`):
21
+ * - No Router / ActivatedRoute imports
22
+ * - All state flows through @Input/@Output
23
+ * - Parent owns persistence coordination if needed — rows are saved in-place on edit
24
+ *
25
+ * ## Runtime-generic by design (re P6.2 in plans/search-scopes-rag-plus/RAG_plan.md)
26
+ *
27
+ * The plan asked whether this grid should adopt the strongly-typed entity API
28
+ * (`record.Field` accessors) instead of `BaseEntity.Get/Set`. It cannot:
29
+ * the component is parameterized by `ChildEntityName` and `Columns[].Field`,
30
+ * both runtime strings, so the entity class is unknown at compile time.
31
+ * Picking a concrete type would force one consumer per child-entity flavor
32
+ * (SearchScopeProvider, SearchScopeEntity, SearchScopeExternalIndex,
33
+ * SearchScopeStorageAccount, AIAgentSearchScope, plus future ones), defeating
34
+ * the point of the abstraction.
35
+ *
36
+ * Mitigation: a runtime sanity check (`Entity.GetFieldByName`) confirms each
37
+ * column's `Field` name exists on the loaded entity before any Get/Set is
38
+ * issued. This catches typos / schema drift at first render rather than
39
+ * silently writing to a non-existent property. See `assertFieldsExist()`.
40
+ *
41
+ * @module @memberjunction/ng-search
42
+ */
43
+ import { ChangeDetectorRef, Component, EventEmitter, Input, Output, inject } from '@angular/core';
44
+ import { LogError, Metadata, RunView } from '@memberjunction/core';
45
+ import { UUIDsEqual } from '@memberjunction/global';
46
+ import * as i0 from "@angular/core";
47
+ import * as i1 from "@angular/forms";
48
+ const _forTrack0 = ($index, $item) => $item.Field;
49
+ const _forTrack1 = ($index, $item) => $item.Entity;
50
+ const _forTrack2 = ($index, $item) => $item.ID;
51
+ const _forTrack3 = ($index, $item) => $item.Value;
52
+ function SearchScopeChildGridComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
53
+ i0.ɵɵelementStart(0, "div", 1);
54
+ i0.ɵɵtext(1, "Loading\u2026");
55
+ i0.ɵɵelementEnd();
56
+ } }
57
+ function SearchScopeChildGridComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
58
+ i0.ɵɵelementStart(0, "div", 2);
59
+ i0.ɵɵelement(1, "i", 3);
60
+ i0.ɵɵtext(2);
61
+ i0.ɵɵelementEnd();
62
+ } if (rf & 2) {
63
+ const ctx_r0 = i0.ɵɵnextContext();
64
+ i0.ɵɵadvance(2);
65
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.LoadError, " ");
66
+ } }
67
+ function SearchScopeChildGridComponent_Conditional_3_For_4_Template(rf, ctx) { if (rf & 1) {
68
+ i0.ɵɵelementStart(0, "th");
69
+ i0.ɵɵtext(1);
70
+ i0.ɵɵelementEnd();
71
+ } if (rf & 2) {
72
+ const col_r2 = ctx.$implicit;
73
+ i0.ɵɵstyleProp("width", col_r2.Width || null);
74
+ i0.ɵɵadvance();
75
+ i0.ɵɵtextInterpolate(col_r2.Label);
76
+ } }
77
+ function SearchScopeChildGridComponent_Conditional_3_Conditional_7_Template(rf, ctx) { if (rf & 1) {
78
+ i0.ɵɵelementStart(0, "tr")(1, "td", 8);
79
+ i0.ɵɵtext(2);
80
+ i0.ɵɵelementEnd()();
81
+ } if (rf & 2) {
82
+ const ctx_r0 = i0.ɵɵnextContext(2);
83
+ i0.ɵɵadvance();
84
+ i0.ɵɵattribute("colspan", ctx_r0.Columns.length + 1);
85
+ i0.ɵɵadvance();
86
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.EmptyMessage, " ");
87
+ } }
88
+ function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_1_Template(rf, ctx) { if (rf & 1) {
89
+ const _r4 = i0.ɵɵgetCurrentView();
90
+ i0.ɵɵelementStart(0, "input", 19);
91
+ i0.ɵɵlistener("change", function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_1_Template_input_change_0_listener($event) { i0.ɵɵrestoreView(_r4); const col_r5 = i0.ɵɵnextContext().$implicit; const row_r6 = i0.ɵɵnextContext().$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.SetCellValue(row_r6, col_r5, $event.target.checked)); });
92
+ i0.ɵɵelementEnd();
93
+ } if (rf & 2) {
94
+ const col_r5 = i0.ɵɵnextContext().$implicit;
95
+ const row_r6 = i0.ɵɵnextContext().$implicit;
96
+ const ctx_r0 = i0.ɵɵnextContext(3);
97
+ i0.ɵɵproperty("checked", !!ctx_r0.GetCellValue(row_r6, col_r5))("disabled", ctx_r0.Disabled || col_r5.ReadOnly);
98
+ } }
99
+ function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_2_Template(rf, ctx) { if (rf & 1) {
100
+ const _r7 = i0.ɵɵgetCurrentView();
101
+ i0.ɵɵelementStart(0, "input", 20);
102
+ i0.ɵɵlistener("change", function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_2_Template_input_change_0_listener($event) { i0.ɵɵrestoreView(_r7); const col_r5 = i0.ɵɵnextContext().$implicit; const row_r6 = i0.ɵɵnextContext().$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.SetCellValue(row_r6, col_r5, $event.target.valueAsNumber)); });
103
+ i0.ɵɵelementEnd();
104
+ } if (rf & 2) {
105
+ const col_r5 = i0.ɵɵnextContext().$implicit;
106
+ const row_r6 = i0.ɵɵnextContext().$implicit;
107
+ const ctx_r0 = i0.ɵɵnextContext(3);
108
+ i0.ɵɵproperty("value", ctx_r0.GetCellValue(row_r6, col_r5) ?? "")("placeholder", col_r5.Placeholder || "")("disabled", ctx_r0.Disabled || col_r5.ReadOnly);
109
+ } }
110
+ function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_3_Template(rf, ctx) { if (rf & 1) {
111
+ const _r8 = i0.ɵɵgetCurrentView();
112
+ i0.ɵɵelementStart(0, "textarea", 21);
113
+ i0.ɵɵlistener("change", function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_3_Template_textarea_change_0_listener($event) { i0.ɵɵrestoreView(_r8); const col_r5 = i0.ɵɵnextContext().$implicit; const row_r6 = i0.ɵɵnextContext().$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.SetCellValue(row_r6, col_r5, $event.target.value)); });
114
+ i0.ɵɵelementEnd();
115
+ } if (rf & 2) {
116
+ const col_r5 = i0.ɵɵnextContext().$implicit;
117
+ const row_r6 = i0.ɵɵnextContext().$implicit;
118
+ const ctx_r0 = i0.ɵɵnextContext(3);
119
+ i0.ɵɵproperty("value", ctx_r0.GetCellValue(row_r6, col_r5) || "")("placeholder", col_r5.Placeholder || "")("disabled", ctx_r0.Disabled || col_r5.ReadOnly);
120
+ } }
121
+ function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_4_Template(rf, ctx) { if (rf & 1) {
122
+ const _r9 = i0.ɵɵgetCurrentView();
123
+ i0.ɵɵelementStart(0, "textarea", 22);
124
+ i0.ɵɵlistener("change", function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_4_Template_textarea_change_0_listener($event) { i0.ɵɵrestoreView(_r9); const col_r5 = i0.ɵɵnextContext().$implicit; const row_r6 = i0.ɵɵnextContext().$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.SetCellValue(row_r6, col_r5, $event.target.value)); });
125
+ i0.ɵɵelementEnd();
126
+ } if (rf & 2) {
127
+ const col_r5 = i0.ɵɵnextContext().$implicit;
128
+ const row_r6 = i0.ɵɵnextContext().$implicit;
129
+ const ctx_r0 = i0.ɵɵnextContext(3);
130
+ i0.ɵɵproperty("value", ctx_r0.GetCellValue(row_r6, col_r5) || "")("placeholder", col_r5.Placeholder || "")("disabled", ctx_r0.Disabled || col_r5.ReadOnly);
131
+ } }
132
+ function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_5_For_4_Template(rf, ctx) { if (rf & 1) {
133
+ i0.ɵɵelementStart(0, "option", 25);
134
+ i0.ɵɵtext(1);
135
+ i0.ɵɵelementEnd();
136
+ } if (rf & 2) {
137
+ const opt_r11 = ctx.$implicit;
138
+ i0.ɵɵproperty("value", opt_r11.ID);
139
+ i0.ɵɵadvance();
140
+ i0.ɵɵtextInterpolate(opt_r11.Label);
141
+ } }
142
+ function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_5_Template(rf, ctx) { if (rf & 1) {
143
+ const _r10 = i0.ɵɵgetCurrentView();
144
+ i0.ɵɵelementStart(0, "select", 23);
145
+ i0.ɵɵlistener("change", function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_5_Template_select_change_0_listener($event) { i0.ɵɵrestoreView(_r10); const col_r5 = i0.ɵɵnextContext().$implicit; const row_r6 = i0.ɵɵnextContext().$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.SetCellValue(row_r6, col_r5, $event.target.value)); });
146
+ i0.ɵɵelementStart(1, "option", 24);
147
+ i0.ɵɵtext(2, "\u2014 select \u2014");
148
+ i0.ɵɵelementEnd();
149
+ i0.ɵɵrepeaterCreate(3, SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_5_For_4_Template, 2, 2, "option", 25, _forTrack2);
150
+ i0.ɵɵelementEnd();
151
+ } if (rf & 2) {
152
+ const col_r5 = i0.ɵɵnextContext().$implicit;
153
+ const row_r6 = i0.ɵɵnextContext().$implicit;
154
+ const ctx_r0 = i0.ɵɵnextContext(3);
155
+ i0.ɵɵproperty("value", ctx_r0.GetCellValue(row_r6, col_r5) || "")("disabled", ctx_r0.Disabled || col_r5.ReadOnly);
156
+ i0.ɵɵadvance(3);
157
+ i0.ɵɵrepeater(ctx_r0.GetLookupOptions(col_r5));
158
+ } }
159
+ function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_6_For_2_Template(rf, ctx) { if (rf & 1) {
160
+ i0.ɵɵelementStart(0, "option", 25);
161
+ i0.ɵɵtext(1);
162
+ i0.ɵɵelementEnd();
163
+ } if (rf & 2) {
164
+ const opt_r13 = ctx.$implicit;
165
+ i0.ɵɵproperty("value", opt_r13.Value);
166
+ i0.ɵɵadvance();
167
+ i0.ɵɵtextInterpolate(opt_r13.Label);
168
+ } }
169
+ function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_6_Template(rf, ctx) { if (rf & 1) {
170
+ const _r12 = i0.ɵɵgetCurrentView();
171
+ i0.ɵɵelementStart(0, "select", 23);
172
+ i0.ɵɵlistener("change", function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_6_Template_select_change_0_listener($event) { i0.ɵɵrestoreView(_r12); const col_r5 = i0.ɵɵnextContext().$implicit; const row_r6 = i0.ɵɵnextContext().$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.SetCellValue(row_r6, col_r5, $event.target.value)); });
173
+ i0.ɵɵrepeaterCreate(1, SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_6_For_2_Template, 2, 2, "option", 25, _forTrack3);
174
+ i0.ɵɵelementEnd();
175
+ } if (rf & 2) {
176
+ const col_r5 = i0.ɵɵnextContext().$implicit;
177
+ const row_r6 = i0.ɵɵnextContext().$implicit;
178
+ const ctx_r0 = i0.ɵɵnextContext(3);
179
+ i0.ɵɵproperty("value", ctx_r0.GetCellValue(row_r6, col_r5) ?? "")("disabled", ctx_r0.Disabled || col_r5.ReadOnly);
180
+ i0.ɵɵadvance();
181
+ i0.ɵɵrepeater(col_r5.Options);
182
+ } }
183
+ function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_7_Template(rf, ctx) { if (rf & 1) {
184
+ const _r14 = i0.ɵɵgetCurrentView();
185
+ i0.ɵɵelementStart(0, "input", 26);
186
+ i0.ɵɵlistener("change", function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_7_Template_input_change_0_listener($event) { i0.ɵɵrestoreView(_r14); const col_r5 = i0.ɵɵnextContext().$implicit; const row_r6 = i0.ɵɵnextContext().$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.SetCellValue(row_r6, col_r5, $event.target.value)); });
187
+ i0.ɵɵelementEnd();
188
+ } if (rf & 2) {
189
+ const col_r5 = i0.ɵɵnextContext().$implicit;
190
+ const row_r6 = i0.ɵɵnextContext().$implicit;
191
+ const ctx_r0 = i0.ɵɵnextContext(3);
192
+ i0.ɵɵproperty("value", ctx_r0.GetCellValue(row_r6, col_r5) || "")("placeholder", col_r5.Placeholder || "")("disabled", ctx_r0.Disabled || col_r5.ReadOnly);
193
+ } }
194
+ function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Template(rf, ctx) { if (rf & 1) {
195
+ i0.ɵɵelementStart(0, "td");
196
+ i0.ɵɵconditionalCreate(1, SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_1_Template, 1, 2, "input", 13)(2, SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_2_Template, 1, 3, "input", 14)(3, SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_3_Template, 1, 3, "textarea", 15)(4, SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_4_Template, 1, 3, "textarea", 16)(5, SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_5_Template, 5, 2, "select", 17)(6, SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_6_Template, 3, 2, "select", 17)(7, SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Case_7_Template, 1, 3, "input", 18);
197
+ i0.ɵɵelementEnd();
198
+ } if (rf & 2) {
199
+ let tmp_23_0;
200
+ const col_r5 = ctx.$implicit;
201
+ i0.ɵɵstyleProp("width", col_r5.Width || null);
202
+ i0.ɵɵadvance();
203
+ i0.ɵɵconditional((tmp_23_0 = col_r5.Type) === "checkbox" ? 1 : tmp_23_0 === "number" ? 2 : tmp_23_0 === "textarea" ? 3 : tmp_23_0 === "code" ? 4 : tmp_23_0 === "lookup" ? 5 : tmp_23_0 === "select" ? 6 : 7);
204
+ } }
205
+ function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_Template(rf, ctx) { if (rf & 1) {
206
+ const _r3 = i0.ɵɵgetCurrentView();
207
+ i0.ɵɵelementStart(0, "tr");
208
+ i0.ɵɵrepeaterCreate(1, SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_For_2_Template, 8, 3, "td", 5, _forTrack0);
209
+ i0.ɵɵelementStart(3, "td", 10)(4, "button", 11);
210
+ i0.ɵɵlistener("click", function SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_Template_button_click_4_listener() { const row_r6 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.RemoveRow(row_r6)); });
211
+ i0.ɵɵelement(5, "i", 12);
212
+ i0.ɵɵelementEnd()()();
213
+ } if (rf & 2) {
214
+ const row_r6 = ctx.$implicit;
215
+ const ctx_r0 = i0.ɵɵnextContext(3);
216
+ i0.ɵɵclassProp("saving", row_r6.Saving);
217
+ i0.ɵɵadvance();
218
+ i0.ɵɵrepeater(ctx_r0.Columns);
219
+ i0.ɵɵadvance(3);
220
+ i0.ɵɵproperty("disabled", ctx_r0.Disabled || row_r6.Saving);
221
+ } }
222
+ function SearchScopeChildGridComponent_Conditional_3_Conditional_8_Template(rf, ctx) { if (rf & 1) {
223
+ i0.ɵɵrepeaterCreate(0, SearchScopeChildGridComponent_Conditional_3_Conditional_8_For_1_Template, 6, 3, "tr", 9, _forTrack1);
224
+ } if (rf & 2) {
225
+ const ctx_r0 = i0.ɵɵnextContext(2);
226
+ i0.ɵɵrepeater(ctx_r0.Rows);
227
+ } }
228
+ function SearchScopeChildGridComponent_Conditional_3_Conditional_9_Template(rf, ctx) { if (rf & 1) {
229
+ const _r15 = i0.ɵɵgetCurrentView();
230
+ i0.ɵɵelementStart(0, "div", 7)(1, "button", 27);
231
+ i0.ɵɵlistener("click", function SearchScopeChildGridComponent_Conditional_3_Conditional_9_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r15); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.AddRow()); });
232
+ i0.ɵɵtext(2);
233
+ i0.ɵɵelementEnd()();
234
+ } if (rf & 2) {
235
+ const ctx_r0 = i0.ɵɵnextContext(2);
236
+ i0.ɵɵadvance(2);
237
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.AddButtonLabel, " ");
238
+ } }
239
+ function SearchScopeChildGridComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
240
+ i0.ɵɵelementStart(0, "table", 4)(1, "thead")(2, "tr");
241
+ i0.ɵɵrepeaterCreate(3, SearchScopeChildGridComponent_Conditional_3_For_4_Template, 2, 3, "th", 5, _forTrack0);
242
+ i0.ɵɵelement(5, "th", 6);
243
+ i0.ɵɵelementEnd()();
244
+ i0.ɵɵelementStart(6, "tbody");
245
+ i0.ɵɵconditionalCreate(7, SearchScopeChildGridComponent_Conditional_3_Conditional_7_Template, 3, 2, "tr")(8, SearchScopeChildGridComponent_Conditional_3_Conditional_8_Template, 2, 0);
246
+ i0.ɵɵelementEnd()();
247
+ i0.ɵɵconditionalCreate(9, SearchScopeChildGridComponent_Conditional_3_Conditional_9_Template, 3, 1, "div", 7);
248
+ } if (rf & 2) {
249
+ const ctx_r0 = i0.ɵɵnextContext();
250
+ i0.ɵɵadvance(3);
251
+ i0.ɵɵrepeater(ctx_r0.Columns);
252
+ i0.ɵɵadvance(4);
253
+ i0.ɵɵconditional(ctx_r0.Rows.length === 0 ? 7 : 8);
254
+ i0.ɵɵadvance(2);
255
+ i0.ɵɵconditional(!ctx_r0.Disabled ? 9 : -1);
256
+ } }
257
+ export class SearchScopeChildGridComponent {
258
+ cdr = inject(ChangeDetectorRef);
259
+ // ─── Inputs ───────────────────────────────────────────────────────────────
260
+ _parentID = null;
261
+ /**
262
+ * The parent record ID to filter / attach rows to. For a `MJ: Search Scopes` parent
263
+ * this is the scope ID; for an `MJ: AI Agents` parent (assigning scopes to an agent)
264
+ * this is the agent ID. Paired with `ParentFieldName` which names the FK column.
265
+ */
266
+ set ParentID(value) {
267
+ if (value === this._parentID)
268
+ return;
269
+ this._parentID = value;
270
+ if (value) {
271
+ void this.LoadRows();
272
+ }
273
+ else {
274
+ this.Rows = [];
275
+ }
276
+ }
277
+ get ParentID() {
278
+ return this._parentID;
279
+ }
280
+ /** MJ entity name of the child table (e.g. `MJ: Search Scope Providers`). */
281
+ ChildEntityName = '';
282
+ /** FK column on the child that references the parent SearchScope. */
283
+ ParentFieldName = 'SearchScopeID';
284
+ /** Column config — drives rendering of each row. */
285
+ Columns = [];
286
+ /** Button label for the "add new row" action. */
287
+ AddButtonLabel = '+ Add row';
288
+ /** Empty-state copy when the grid has zero rows. */
289
+ EmptyMessage = 'No rows yet.';
290
+ /** Optional OrderBy clause applied to the initial load. */
291
+ OrderBy = '';
292
+ /** Disable all editing (read-only mode). */
293
+ Disabled = false;
294
+ // ─── Outputs ──────────────────────────────────────────────────────────────
295
+ /** Emitted whenever a row is successfully added or removed. */
296
+ RowsChanged = new EventEmitter();
297
+ /** Emitted after a row is added. */
298
+ RowAdded = new EventEmitter();
299
+ /** Emitted after a row is removed. */
300
+ RowRemoved = new EventEmitter();
301
+ // ─── State ────────────────────────────────────────────────────────────────
302
+ Rows = [];
303
+ IsLoading = false;
304
+ LoadError = null;
305
+ /** Cached lookup options by lookup entity name. */
306
+ lookupCache = new Map();
307
+ ngOnDestroy() {
308
+ // No subscriptions to clean up yet; kept for future compatibility.
309
+ }
310
+ // ─── Public API ──────────────────────────────────────────────────────────
311
+ /** Reload the grid from the server. */
312
+ async LoadRows() {
313
+ if (!this._parentID || !this.ChildEntityName || !this.ParentFieldName) {
314
+ this.Rows = [];
315
+ return;
316
+ }
317
+ this.IsLoading = true;
318
+ this.LoadError = null;
319
+ try {
320
+ const rv = new RunView();
321
+ const result = await rv.RunView({
322
+ EntityName: this.ChildEntityName,
323
+ ExtraFilter: `${this.ParentFieldName} = '${this._parentID.replace(/'/g, "''")}'`,
324
+ OrderBy: this.OrderBy || '__mj_CreatedAt ASC',
325
+ ResultType: 'entity_object'
326
+ });
327
+ if (!result.Success) {
328
+ this.LoadError = result.ErrorMessage ?? 'Failed to load rows';
329
+ this.Rows = [];
330
+ }
331
+ else {
332
+ const rows = result.Results || [];
333
+ // Runtime sanity check: every column's Field must be a real
334
+ // property on the loaded entity. Catches schema drift between
335
+ // a parent form's Columns spec and the actual entity shape
336
+ // before the first Get/Set fires.
337
+ if (rows.length > 0) {
338
+ this.assertFieldsExist(rows[0]);
339
+ }
340
+ this.Rows = rows.map(entity => this.makeRow(entity));
341
+ await this.hydrateLookups();
342
+ }
343
+ }
344
+ catch (err) {
345
+ this.LoadError = err instanceof Error ? err.message : String(err);
346
+ this.Rows = [];
347
+ }
348
+ finally {
349
+ this.IsLoading = false;
350
+ this.cdr.detectChanges();
351
+ }
352
+ }
353
+ /** Add a new empty row attached to the current scope. */
354
+ async AddRow() {
355
+ if (this.Disabled || !this._parentID || !this.ChildEntityName)
356
+ return;
357
+ try {
358
+ const md = new Metadata(); // global-provider-ok: child grid does not yet accept a Provider input — multi-provider threading is the team's separate Phase 6 effort
359
+ const entity = await md.GetEntityObject(this.ChildEntityName);
360
+ entity.Set(this.ParentFieldName, this._parentID);
361
+ this.applyColumnDefaults(entity);
362
+ this.Rows = [...this.Rows, this.makeRow(entity)];
363
+ this.cdr.detectChanges();
364
+ }
365
+ catch (err) {
366
+ LogError(`SearchScopeChildGrid: failed to create new ${this.ChildEntityName} row — ${err instanceof Error ? err.message : String(err)}`);
367
+ }
368
+ }
369
+ /** Persist changes to a row. Returns true on success. */
370
+ async SaveRow(row) {
371
+ if (this.Disabled)
372
+ return false;
373
+ row.Saving = true;
374
+ try {
375
+ const ok = await row.Entity.Save();
376
+ if (!ok) {
377
+ LogError(`SearchScopeChildGrid: save failed for ${this.ChildEntityName}: ${row.Entity.LatestResult?.CompleteMessage ?? 'unknown error'}`);
378
+ return false;
379
+ }
380
+ this.RowsChanged.emit();
381
+ // If this was a brand-new row, emit the Added event on first successful save.
382
+ if (!row.Entity.IsSaved)
383
+ return true;
384
+ this.RowAdded.emit(row.Entity);
385
+ return true;
386
+ }
387
+ finally {
388
+ row.Saving = false;
389
+ this.cdr.detectChanges();
390
+ }
391
+ }
392
+ /** Remove a row (deletes from DB if saved, drops from state if new). */
393
+ async RemoveRow(row) {
394
+ if (this.Disabled)
395
+ return;
396
+ try {
397
+ if (row.Entity.IsSaved) {
398
+ const ok = await row.Entity.Delete();
399
+ if (!ok) {
400
+ LogError(`SearchScopeChildGrid: delete failed for ${this.ChildEntityName}: ${row.Entity.LatestResult?.CompleteMessage ?? 'unknown error'}`);
401
+ return;
402
+ }
403
+ }
404
+ this.Rows = this.Rows.filter(r => r !== row);
405
+ this.RowRemoved.emit(row.Entity);
406
+ this.RowsChanged.emit();
407
+ this.cdr.detectChanges();
408
+ }
409
+ catch (err) {
410
+ LogError(`SearchScopeChildGrid: remove error — ${err instanceof Error ? err.message : String(err)}`);
411
+ }
412
+ }
413
+ /** Read a cell value from a row (used by the template). */
414
+ GetCellValue(row, column) {
415
+ return row.Entity.Get(column.Field);
416
+ }
417
+ /**
418
+ * Write a cell value back to the row and persist. Lookup columns store the ID but
419
+ * we also refresh the cached display label so the UI stays in sync without a reload.
420
+ */
421
+ async SetCellValue(row, column, value) {
422
+ if (this.Disabled || column.ReadOnly)
423
+ return;
424
+ row.Entity.Set(column.Field, value);
425
+ if (column.Type === 'lookup' && typeof value === 'string') {
426
+ const options = column.LookupEntityName ? this.lookupCache.get(column.LookupEntityName) : undefined;
427
+ const match = options?.find(o => UUIDsEqual(o.ID, value));
428
+ row.LookupLabels[column.Field] = match?.Label ?? '';
429
+ }
430
+ await this.SaveRow(row);
431
+ }
432
+ /** Return the display label for a lookup cell. */
433
+ GetLookupLabel(row, column) {
434
+ return row.LookupLabels[column.Field] ?? '';
435
+ }
436
+ /** Return the dropdown options for a lookup column. */
437
+ GetLookupOptions(column) {
438
+ if (!column.LookupEntityName)
439
+ return [];
440
+ return this.lookupCache.get(column.LookupEntityName) ?? [];
441
+ }
442
+ // ─── Internals ───────────────────────────────────────────────────────────
443
+ /**
444
+ * Load the set of lookup-option arrays referenced by `Columns` so dropdowns render
445
+ * without triggering additional RunView calls per row.
446
+ */
447
+ async hydrateLookups() {
448
+ const lookupColumns = this.Columns.filter(c => c.Type === 'lookup' && c.LookupEntityName);
449
+ const toLoad = lookupColumns.filter(c => !this.lookupCache.has(c.LookupEntityName));
450
+ if (toLoad.length === 0) {
451
+ this.refreshLookupLabels();
452
+ return;
453
+ }
454
+ const rv = new RunView();
455
+ const queries = toLoad.map(c => ({
456
+ EntityName: c.LookupEntityName,
457
+ ExtraFilter: c.LookupFilter || '',
458
+ OrderBy: `${c.LookupDisplayField ?? 'Name'} ASC`,
459
+ Fields: ['ID', c.LookupDisplayField ?? 'Name'],
460
+ ResultType: 'simple',
461
+ }));
462
+ try {
463
+ const results = await rv.RunViews(queries);
464
+ toLoad.forEach((col, idx) => {
465
+ const res = results[idx];
466
+ if (!res.Success) {
467
+ LogError(`SearchScopeChildGrid: lookup load failed for ${col.LookupEntityName}: ${res.ErrorMessage}`);
468
+ this.lookupCache.set(col.LookupEntityName, []);
469
+ return;
470
+ }
471
+ const displayField = col.LookupDisplayField ?? 'Name';
472
+ const options = res.Results.map(r => ({
473
+ ID: String(r['ID']),
474
+ Label: String(r[displayField] ?? r['ID']),
475
+ }));
476
+ this.lookupCache.set(col.LookupEntityName, options);
477
+ });
478
+ }
479
+ catch (err) {
480
+ LogError(`SearchScopeChildGrid: hydrateLookups error — ${err instanceof Error ? err.message : String(err)}`);
481
+ }
482
+ this.refreshLookupLabels();
483
+ }
484
+ refreshLookupLabels() {
485
+ for (const row of this.Rows) {
486
+ for (const col of this.Columns) {
487
+ if (col.Type !== 'lookup' || !col.LookupEntityName)
488
+ continue;
489
+ const id = row.Entity.Get(col.Field);
490
+ if (typeof id !== 'string')
491
+ continue;
492
+ const options = this.lookupCache.get(col.LookupEntityName) ?? [];
493
+ const match = options.find(o => UUIDsEqual(o.ID, id));
494
+ row.LookupLabels[col.Field] = match?.Label ?? '';
495
+ }
496
+ }
497
+ }
498
+ makeRow(entity) {
499
+ return {
500
+ Entity: entity,
501
+ LookupLabels: {},
502
+ Saving: false,
503
+ };
504
+ }
505
+ /** Apply `DefaultValue` from column specs when creating a new row. */
506
+ applyColumnDefaults(entity) {
507
+ for (const col of this.Columns) {
508
+ if (col.Type === 'checkbox' && entity.Get(col.Field) === null) {
509
+ entity.Set(col.Field, true);
510
+ }
511
+ }
512
+ }
513
+ /**
514
+ * Sanity-check that every column's `Field` exists on the loaded entity.
515
+ * The component is runtime-generic by design, but each consumer's `Columns`
516
+ * spec must match the actual entity shape; otherwise `entity.Get(col.Field)`
517
+ * silently returns null and `entity.Set(col.Field, ...)` silently no-ops,
518
+ * masking schema drift. Logging here surfaces the mismatch at first render.
519
+ *
520
+ * `entity.Fields` is the canonical metadata-driven list of fields the
521
+ * BaseEntity subclass exposes. Comparing the column spec's `Field` against
522
+ * that list catches typos and stale Columns arrays in custom forms.
523
+ */
524
+ assertFieldsExist(sample) {
525
+ const knownFields = new Set(sample.Fields.map(f => f.Name.toLowerCase()));
526
+ const unknown = this.Columns
527
+ .filter(c => !c.ReadOnly)
528
+ .map(c => c.Field)
529
+ .filter(f => !knownFields.has(f.toLowerCase()));
530
+ if (unknown.length > 0) {
531
+ LogError(`SearchScopeChildGridComponent: Columns spec references fields ` +
532
+ `not found on entity '${this.ChildEntityName}': ${unknown.join(', ')}. ` +
533
+ `This indicates schema drift — update the parent form's Columns ` +
534
+ `array to match the entity's typed properties.`);
535
+ }
536
+ }
537
+ static ɵfac = function SearchScopeChildGridComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SearchScopeChildGridComponent)(); };
538
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SearchScopeChildGridComponent, selectors: [["mj-search-scope-child-grid"]], inputs: { ParentID: "ParentID", ChildEntityName: "ChildEntityName", ParentFieldName: "ParentFieldName", Columns: "Columns", AddButtonLabel: "AddButtonLabel", EmptyMessage: "EmptyMessage", OrderBy: "OrderBy", Disabled: "Disabled" }, outputs: { RowsChanged: "RowsChanged", RowAdded: "RowAdded", RowRemoved: "RowRemoved" }, standalone: false, decls: 4, vars: 3, consts: [[1, "scope-grid"], [1, "scope-grid-loading"], [1, "scope-grid-error"], ["aria-hidden", "true", 1, "fa-solid", "fa-triangle-exclamation"], [1, "scope-grid-table"], [3, "width"], ["aria-hidden", "true", 1, "scope-grid-actions-col"], [1, "scope-grid-footer"], [1, "scope-grid-empty"], [3, "saving"], [1, "scope-grid-actions-col"], ["type", "button", "title", "Remove row", 1, "scope-grid-icon-btn", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-trash"], ["type", "checkbox", 1, "mj-checkbox", 3, "checked", "disabled"], ["type", "number", 1, "mj-input", "scope-grid-input", 3, "value", "placeholder", "disabled"], ["rows", "2", 1, "mj-textarea", "scope-grid-textarea", 3, "value", "placeholder", "disabled"], ["rows", "2", "spellcheck", "false", 1, "mj-textarea", "scope-grid-code", 3, "value", "placeholder", "disabled"], [1, "mj-input", "scope-grid-select", 3, "value", "disabled"], ["type", "text", 1, "mj-input", "scope-grid-input", 3, "value", "placeholder", "disabled"], ["type", "checkbox", 1, "mj-checkbox", 3, "change", "checked", "disabled"], ["type", "number", 1, "mj-input", "scope-grid-input", 3, "change", "value", "placeholder", "disabled"], ["rows", "2", 1, "mj-textarea", "scope-grid-textarea", 3, "change", "value", "placeholder", "disabled"], ["rows", "2", "spellcheck", "false", 1, "mj-textarea", "scope-grid-code", 3, "change", "value", "placeholder", "disabled"], [1, "mj-input", "scope-grid-select", 3, "change", "value", "disabled"], ["value", ""], [3, "value"], ["type", "text", 1, "mj-input", "scope-grid-input", 3, "change", "value", "placeholder", "disabled"], ["type", "button", 1, "scope-grid-add-btn", 3, "click"]], template: function SearchScopeChildGridComponent_Template(rf, ctx) { if (rf & 1) {
539
+ i0.ɵɵelementStart(0, "div", 0);
540
+ i0.ɵɵconditionalCreate(1, SearchScopeChildGridComponent_Conditional_1_Template, 2, 0, "div", 1)(2, SearchScopeChildGridComponent_Conditional_2_Template, 3, 1, "div", 2)(3, SearchScopeChildGridComponent_Conditional_3_Template, 10, 2);
541
+ i0.ɵɵelementEnd();
542
+ } if (rf & 2) {
543
+ i0.ɵɵclassProp("disabled", ctx.Disabled);
544
+ i0.ɵɵadvance();
545
+ i0.ɵɵconditional(ctx.IsLoading ? 1 : ctx.LoadError ? 2 : 3);
546
+ } }, dependencies: [i1.NgSelectOption, i1.ɵNgSelectMultipleOption], styles: [".scope-grid[_ngcontent-%COMP%] {\n width: 100%;\n font-family: inherit;\n color: var(--mj-text-primary);\n}\n\n.scope-grid.disabled[_ngcontent-%COMP%] {\n opacity: 0.7;\n pointer-events: none;\n}\n\n.scope-grid-loading[_ngcontent-%COMP%], \n.scope-grid-error[_ngcontent-%COMP%], \n.scope-grid-empty[_ngcontent-%COMP%] {\n padding: 16px;\n text-align: center;\n color: var(--mj-text-muted);\n font-size: 0.875rem;\n}\n\n.scope-grid-error[_ngcontent-%COMP%] {\n color: var(--mj-status-error-text);\n background: var(--mj-status-error-bg);\n border: 1px solid var(--mj-status-error-border);\n border-radius: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n}\n\n.scope-grid-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: separate;\n border-spacing: 0;\n font-size: 0.875rem;\n}\n\n.scope-grid-table[_ngcontent-%COMP%] thead[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n text-align: left;\n padding: 8px 10px;\n border-bottom: 1px solid var(--mj-border-default);\n color: var(--mj-text-secondary);\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n background: var(--mj-bg-surface-card);\n}\n\n.scope-grid-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 6px 10px;\n border-bottom: 1px solid var(--mj-border-subtle);\n vertical-align: top;\n}\n\n.scope-grid-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr.saving[_ngcontent-%COMP%] {\n opacity: 0.6;\n}\n\n.scope-grid-actions-col[_ngcontent-%COMP%] {\n width: 40px;\n text-align: right;\n}\n\n.scope-grid-input[_ngcontent-%COMP%], \n.scope-grid-select[_ngcontent-%COMP%] {\n width: 100%;\n font-size: 0.85rem;\n}\n\n.scope-grid-textarea[_ngcontent-%COMP%], \n.scope-grid-code[_ngcontent-%COMP%] {\n width: 100%;\n resize: vertical;\n font-size: 0.85rem;\n}\n\n.scope-grid-code[_ngcontent-%COMP%] {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n padding: 6px 8px;\n}\n\n.scope-grid-icon-btn[_ngcontent-%COMP%] {\n background: transparent;\n border: none;\n color: var(--mj-text-muted);\n cursor: pointer;\n padding: 6px;\n border-radius: 4px;\n}\n\n.scope-grid-icon-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n color: var(--mj-status-error-text);\n background: var(--mj-bg-surface-hover);\n}\n\n.scope-grid-icon-btn[_ngcontent-%COMP%]:disabled {\n color: var(--mj-text-disabled);\n cursor: not-allowed;\n}\n\n.scope-grid-footer[_ngcontent-%COMP%] {\n padding: 10px 6px 2px;\n display: flex;\n justify-content: flex-start;\n}\n\n.scope-grid-add-btn[_ngcontent-%COMP%] {\n background: transparent;\n border: 1px dashed var(--mj-border-default);\n color: var(--mj-text-link);\n padding: 6px 14px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 0.85rem;\n font-weight: 500;\n transition: background 120ms ease, border-color 120ms ease;\n}\n\n.scope-grid-add-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n color: var(--mj-brand-primary);\n}"] });
547
+ }
548
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SearchScopeChildGridComponent, [{
549
+ type: Component,
550
+ args: [{ standalone: false, selector: 'mj-search-scope-child-grid', template: "<div class=\"scope-grid\" [class.disabled]=\"Disabled\">\n @if (IsLoading) {\n <div class=\"scope-grid-loading\">Loading&hellip;</div>\n } @else if (LoadError) {\n <div class=\"scope-grid-error\">\n <i class=\"fa-solid fa-triangle-exclamation\" aria-hidden=\"true\"></i>\n {{ LoadError }}\n </div>\n } @else {\n <table class=\"scope-grid-table\">\n <thead>\n <tr>\n @for (col of Columns; track col.Field) {\n <th [style.width]=\"col.Width || null\">{{ col.Label }}</th>\n }\n <th class=\"scope-grid-actions-col\" aria-hidden=\"true\"></th>\n </tr>\n </thead>\n <tbody>\n @if (Rows.length === 0) {\n <tr>\n <td [attr.colspan]=\"Columns.length + 1\" class=\"scope-grid-empty\">\n {{ EmptyMessage }}\n </td>\n </tr>\n } @else {\n @for (row of Rows; track row.Entity) {\n <tr [class.saving]=\"row.Saving\">\n @for (col of Columns; track col.Field) {\n <td [style.width]=\"col.Width || null\">\n @switch (col.Type) {\n @case ('checkbox') {\n <input\n type=\"checkbox\"\n class=\"mj-checkbox\"\n [checked]=\"!!GetCellValue(row, col)\"\n [disabled]=\"Disabled || col.ReadOnly\"\n (change)=\"SetCellValue(row, col, $any($event.target).checked)\">\n }\n @case ('number') {\n <input\n type=\"number\"\n class=\"mj-input scope-grid-input\"\n [value]=\"$any(GetCellValue(row, col)) ?? ''\"\n [placeholder]=\"col.Placeholder || ''\"\n [disabled]=\"Disabled || col.ReadOnly\"\n (change)=\"SetCellValue(row, col, $any($event.target).valueAsNumber)\">\n }\n @case ('textarea') {\n <textarea\n class=\"mj-textarea scope-grid-textarea\"\n rows=\"2\"\n [value]=\"$any(GetCellValue(row, col)) || ''\"\n [placeholder]=\"col.Placeholder || ''\"\n [disabled]=\"Disabled || col.ReadOnly\"\n (change)=\"SetCellValue(row, col, $any($event.target).value)\"></textarea>\n }\n @case ('code') {\n <textarea\n class=\"mj-textarea scope-grid-code\"\n rows=\"2\"\n spellcheck=\"false\"\n [value]=\"$any(GetCellValue(row, col)) || ''\"\n [placeholder]=\"col.Placeholder || ''\"\n [disabled]=\"Disabled || col.ReadOnly\"\n (change)=\"SetCellValue(row, col, $any($event.target).value)\"></textarea>\n }\n @case ('lookup') {\n <select\n class=\"mj-input scope-grid-select\"\n [value]=\"$any(GetCellValue(row, col)) || ''\"\n [disabled]=\"Disabled || col.ReadOnly\"\n (change)=\"SetCellValue(row, col, $any($event.target).value)\">\n <option value=\"\">\u2014 select \u2014</option>\n @for (opt of GetLookupOptions(col); track opt.ID) {\n <option [value]=\"opt.ID\">{{ opt.Label }}</option>\n }\n </select>\n }\n @case ('select') {\n <select\n class=\"mj-input scope-grid-select\"\n [value]=\"$any(GetCellValue(row, col)) ?? ''\"\n [disabled]=\"Disabled || col.ReadOnly\"\n (change)=\"SetCellValue(row, col, $any($event.target).value)\">\n @for (opt of col.Options; track opt.Value) {\n <option [value]=\"$any(opt.Value)\">{{ opt.Label }}</option>\n }\n </select>\n }\n @default {\n <input\n type=\"text\"\n class=\"mj-input scope-grid-input\"\n [value]=\"$any(GetCellValue(row, col)) || ''\"\n [placeholder]=\"col.Placeholder || ''\"\n [disabled]=\"Disabled || col.ReadOnly\"\n (change)=\"SetCellValue(row, col, $any($event.target).value)\">\n }\n }\n </td>\n }\n <td class=\"scope-grid-actions-col\">\n <button\n type=\"button\"\n class=\"scope-grid-icon-btn\"\n title=\"Remove row\"\n [disabled]=\"Disabled || row.Saving\"\n (click)=\"RemoveRow(row)\">\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n </button>\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n\n @if (!Disabled) {\n <div class=\"scope-grid-footer\">\n <button type=\"button\" class=\"scope-grid-add-btn\" (click)=\"AddRow()\">\n {{ AddButtonLabel }}\n </button>\n </div>\n }\n }\n</div>\n", styles: [".scope-grid {\n width: 100%;\n font-family: inherit;\n color: var(--mj-text-primary);\n}\n\n.scope-grid.disabled {\n opacity: 0.7;\n pointer-events: none;\n}\n\n.scope-grid-loading,\n.scope-grid-error,\n.scope-grid-empty {\n padding: 16px;\n text-align: center;\n color: var(--mj-text-muted);\n font-size: 0.875rem;\n}\n\n.scope-grid-error {\n color: var(--mj-status-error-text);\n background: var(--mj-status-error-bg);\n border: 1px solid var(--mj-status-error-border);\n border-radius: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n}\n\n.scope-grid-table {\n width: 100%;\n border-collapse: separate;\n border-spacing: 0;\n font-size: 0.875rem;\n}\n\n.scope-grid-table thead th {\n text-align: left;\n padding: 8px 10px;\n border-bottom: 1px solid var(--mj-border-default);\n color: var(--mj-text-secondary);\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n background: var(--mj-bg-surface-card);\n}\n\n.scope-grid-table tbody td {\n padding: 6px 10px;\n border-bottom: 1px solid var(--mj-border-subtle);\n vertical-align: top;\n}\n\n.scope-grid-table tbody tr.saving {\n opacity: 0.6;\n}\n\n.scope-grid-actions-col {\n width: 40px;\n text-align: right;\n}\n\n.scope-grid-input,\n.scope-grid-select {\n width: 100%;\n font-size: 0.85rem;\n}\n\n.scope-grid-textarea,\n.scope-grid-code {\n width: 100%;\n resize: vertical;\n font-size: 0.85rem;\n}\n\n.scope-grid-code {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n padding: 6px 8px;\n}\n\n.scope-grid-icon-btn {\n background: transparent;\n border: none;\n color: var(--mj-text-muted);\n cursor: pointer;\n padding: 6px;\n border-radius: 4px;\n}\n\n.scope-grid-icon-btn:hover:not(:disabled) {\n color: var(--mj-status-error-text);\n background: var(--mj-bg-surface-hover);\n}\n\n.scope-grid-icon-btn:disabled {\n color: var(--mj-text-disabled);\n cursor: not-allowed;\n}\n\n.scope-grid-footer {\n padding: 10px 6px 2px;\n display: flex;\n justify-content: flex-start;\n}\n\n.scope-grid-add-btn {\n background: transparent;\n border: 1px dashed var(--mj-border-default);\n color: var(--mj-text-link);\n padding: 6px 14px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 0.85rem;\n font-weight: 500;\n transition: background 120ms ease, border-color 120ms ease;\n}\n\n.scope-grid-add-btn:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n color: var(--mj-brand-primary);\n}\n"] }]
551
+ }], null, { ParentID: [{
552
+ type: Input
553
+ }], ChildEntityName: [{
554
+ type: Input
555
+ }], ParentFieldName: [{
556
+ type: Input
557
+ }], Columns: [{
558
+ type: Input
559
+ }], AddButtonLabel: [{
560
+ type: Input
561
+ }], EmptyMessage: [{
562
+ type: Input
563
+ }], OrderBy: [{
564
+ type: Input
565
+ }], Disabled: [{
566
+ type: Input
567
+ }], RowsChanged: [{
568
+ type: Output
569
+ }], RowAdded: [{
570
+ type: Output
571
+ }], RowRemoved: [{
572
+ type: Output
573
+ }] }); })();
574
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SearchScopeChildGridComponent, { className: "SearchScopeChildGridComponent", filePath: "src/lib/search-scope-child-grid.component.ts", lineNumber: 106 }); })();
575
+ //# sourceMappingURL=search-scope-child-grid.component.js.map