@memberjunction/ng-explorer-settings 5.49.0 → 5.51.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.
@@ -0,0 +1,753 @@
1
+ import { Component, Output, EventEmitter, ChangeDetectorRef, NgZone, ElementRef, inject } from '@angular/core';
2
+ import { LogError, LogStatus, LogStatusEx } from '@memberjunction/core';
3
+ import { UserInfoEngine } from '@memberjunction/core-entities';
4
+ import { ApplicationManager } from '@memberjunction/ng-base-application';
5
+ import { SharedService } from '@memberjunction/ng-shared';
6
+ import { UUIDsEqual } from '@memberjunction/global';
7
+ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
8
+ import { moveAndResequence, resequenceItems } from './user-app-config-reorder';
9
+ import * as i0 from "@angular/core";
10
+ import * as i1 from "@memberjunction/ng-shared-generic";
11
+ import * as i2 from "@memberjunction/ng-ui-components";
12
+ const _forTrack0 = ($index, $item) => $item.app.ID;
13
+ function UserAppConfigContentComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
14
+ i0.ɵɵelementStart(0, "div", 1);
15
+ i0.ɵɵelement(1, "mj-loading", 10);
16
+ i0.ɵɵelementEnd();
17
+ } }
18
+ function UserAppConfigContentComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
19
+ i0.ɵɵelementStart(0, "mj-alert", 2);
20
+ i0.ɵɵtext(1);
21
+ i0.ɵɵelementEnd();
22
+ } if (rf & 2) {
23
+ const ctx_r0 = i0.ɵɵnextContext();
24
+ i0.ɵɵadvance();
25
+ i0.ɵɵtextInterpolate(ctx_r0.ErrorMessage);
26
+ } }
27
+ function UserAppConfigContentComponent_Conditional_3_Conditional_13_Template(rf, ctx) { if (rf & 1) {
28
+ i0.ɵɵelement(0, "mj-empty-state", 19);
29
+ } }
30
+ function UserAppConfigContentComponent_Conditional_3_For_15_Conditional_6_Template(rf, ctx) { if (rf & 1) {
31
+ i0.ɵɵelementStart(0, "span", 32);
32
+ i0.ɵɵtext(1);
33
+ i0.ɵɵelementEnd();
34
+ } if (rf & 2) {
35
+ const item_r4 = i0.ɵɵnextContext().$implicit;
36
+ i0.ɵɵadvance();
37
+ i0.ɵɵtextInterpolate(item_r4.app.Description);
38
+ } }
39
+ function UserAppConfigContentComponent_Conditional_3_For_15_Template(rf, ctx) { if (rf & 1) {
40
+ const _r3 = i0.ɵɵgetCurrentView();
41
+ i0.ɵɵelementStart(0, "div", 27);
42
+ i0.ɵɵlistener("dblclick", function UserAppConfigContentComponent_Conditional_3_For_15_Template_div_dblclick_0_listener() { const item_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.AddApp(item_r4)); });
43
+ i0.ɵɵelementStart(1, "div", 28);
44
+ i0.ɵɵelement(2, "i", 29);
45
+ i0.ɵɵelementEnd();
46
+ i0.ɵɵelementStart(3, "div", 30)(4, "span", 31);
47
+ i0.ɵɵtext(5);
48
+ i0.ɵɵelementEnd();
49
+ i0.ɵɵconditionalCreate(6, UserAppConfigContentComponent_Conditional_3_For_15_Conditional_6_Template, 2, 1, "span", 32);
50
+ i0.ɵɵelementEnd();
51
+ i0.ɵɵelementStart(7, "button", 33);
52
+ i0.ɵɵlistener("click", function UserAppConfigContentComponent_Conditional_3_For_15_Template_button_click_7_listener() { const item_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.AddApp(item_r4)); });
53
+ i0.ɵɵelement(8, "i", 34);
54
+ i0.ɵɵtext(9, " Add ");
55
+ i0.ɵɵelementEnd()();
56
+ } if (rf & 2) {
57
+ const item_r4 = ctx.$implicit;
58
+ i0.ɵɵadvance();
59
+ i0.ɵɵstyleProp("--app-color", item_r4.app.GetColor());
60
+ i0.ɵɵadvance();
61
+ i0.ɵɵclassMap(item_r4.app.Icon || "fa-solid fa-cube");
62
+ i0.ɵɵadvance(3);
63
+ i0.ɵɵtextInterpolate(item_r4.app.Name);
64
+ i0.ɵɵadvance();
65
+ i0.ɵɵconditional(item_r4.app.Description ? 6 : -1);
66
+ i0.ɵɵadvance();
67
+ i0.ɵɵattribute("aria-label", "Add " + item_r4.app.Name);
68
+ } }
69
+ function UserAppConfigContentComponent_Conditional_3_Conditional_25_Template(rf, ctx) { if (rf & 1) {
70
+ i0.ɵɵelement(0, "mj-empty-state", 23);
71
+ } }
72
+ function UserAppConfigContentComponent_Conditional_3_Conditional_26_For_2_Conditional_9_Template(rf, ctx) { if (rf & 1) {
73
+ i0.ɵɵelementStart(0, "span", 32);
74
+ i0.ɵɵtext(1);
75
+ i0.ɵɵelementEnd();
76
+ } if (rf & 2) {
77
+ const item_r8 = i0.ɵɵnextContext().$implicit;
78
+ i0.ɵɵadvance();
79
+ i0.ɵɵtextInterpolate(item_r8.app.Description);
80
+ } }
81
+ function UserAppConfigContentComponent_Conditional_3_Conditional_26_For_2_Template(rf, ctx) { if (rf & 1) {
82
+ const _r6 = i0.ɵɵgetCurrentView();
83
+ i0.ɵɵelementStart(0, "div", 37);
84
+ i0.ɵɵlistener("dragstart", function UserAppConfigContentComponent_Conditional_3_Conditional_26_For_2_Template_div_dragstart_0_listener($event) { const ctx_r6 = i0.ɵɵrestoreView(_r6); const item_r8 = ctx_r6.$implicit; const ɵ$index_82_r9 = ctx_r6.$index; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnDragStart($event, item_r8, ɵ$index_82_r9)); })("dragend", function UserAppConfigContentComponent_Conditional_3_Conditional_26_For_2_Template_div_dragend_0_listener() { i0.ɵɵrestoreView(_r6); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnDragEnd()); })("dragenter", function UserAppConfigContentComponent_Conditional_3_Conditional_26_For_2_Template_div_dragenter_0_listener($event) { const ɵ$index_82_r9 = i0.ɵɵrestoreView(_r6).$index; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnDragEnter($event, ɵ$index_82_r9)); })("dblclick", function UserAppConfigContentComponent_Conditional_3_Conditional_26_For_2_Template_div_dblclick_0_listener($event) { const item_r8 = i0.ɵɵrestoreView(_r6).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); ctx_r0.RemoveApp(item_r8); return i0.ɵɵresetView($event.stopPropagation()); });
85
+ i0.ɵɵelementStart(1, "div", 38)(2, "div", 39);
86
+ i0.ɵɵelement(3, "i", 40);
87
+ i0.ɵɵelementEnd();
88
+ i0.ɵɵelementStart(4, "div", 28);
89
+ i0.ɵɵelement(5, "i", 29);
90
+ i0.ɵɵelementEnd();
91
+ i0.ɵɵelementStart(6, "div", 30)(7, "span", 31);
92
+ i0.ɵɵtext(8);
93
+ i0.ɵɵelementEnd();
94
+ i0.ɵɵconditionalCreate(9, UserAppConfigContentComponent_Conditional_3_Conditional_26_For_2_Conditional_9_Template, 2, 1, "span", 32);
95
+ i0.ɵɵelementEnd();
96
+ i0.ɵɵelementStart(10, "div", 41)(11, "button", 42);
97
+ i0.ɵɵlistener("click", function UserAppConfigContentComponent_Conditional_3_Conditional_26_For_2_Template_button_click_11_listener($event) { const item_r8 = i0.ɵɵrestoreView(_r6).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); ctx_r0.MoveUp(item_r8); return i0.ɵɵresetView($event.stopPropagation()); });
98
+ i0.ɵɵelement(12, "i", 43);
99
+ i0.ɵɵelementEnd();
100
+ i0.ɵɵelementStart(13, "button", 44);
101
+ i0.ɵɵlistener("click", function UserAppConfigContentComponent_Conditional_3_Conditional_26_For_2_Template_button_click_13_listener($event) { const item_r8 = i0.ɵɵrestoreView(_r6).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); ctx_r0.MoveDown(item_r8); return i0.ɵɵresetView($event.stopPropagation()); });
102
+ i0.ɵɵelement(14, "i", 45);
103
+ i0.ɵɵelementEnd();
104
+ i0.ɵɵelementStart(15, "button", 46);
105
+ i0.ɵɵlistener("click", function UserAppConfigContentComponent_Conditional_3_Conditional_26_For_2_Template_button_click_15_listener($event) { const item_r8 = i0.ɵɵrestoreView(_r6).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); ctx_r0.RemoveApp(item_r8); return i0.ɵɵresetView($event.stopPropagation()); });
106
+ i0.ɵɵelement(16, "i", 47);
107
+ i0.ɵɵelementEnd()()()();
108
+ } if (rf & 2) {
109
+ const item_r8 = ctx.$implicit;
110
+ const ɵ$index_82_r9 = ctx.$index;
111
+ const ctx_r0 = i0.ɵɵnextContext(3);
112
+ i0.ɵɵclassProp("dragging", ctx_r0.DraggedItem === item_r8);
113
+ i0.ɵɵadvance(4);
114
+ i0.ɵɵstyleProp("--app-color", item_r8.app.GetColor());
115
+ i0.ɵɵadvance();
116
+ i0.ɵɵclassMap(item_r8.app.Icon || "fa-solid fa-cube");
117
+ i0.ɵɵadvance(3);
118
+ i0.ɵɵtextInterpolate(item_r8.app.Name);
119
+ i0.ɵɵadvance();
120
+ i0.ɵɵconditional(item_r8.app.Description ? 9 : -1);
121
+ i0.ɵɵadvance(2);
122
+ i0.ɵɵproperty("disabled", ɵ$index_82_r9 === 0);
123
+ i0.ɵɵattribute("aria-label", "Move " + item_r8.app.Name + " up");
124
+ i0.ɵɵadvance(2);
125
+ i0.ɵɵproperty("disabled", ɵ$index_82_r9 === ctx_r0.ActiveApps.length - 1);
126
+ i0.ɵɵattribute("aria-label", "Move " + item_r8.app.Name + " down");
127
+ i0.ɵɵadvance(2);
128
+ i0.ɵɵattribute("aria-label", "Remove " + item_r8.app.Name);
129
+ } }
130
+ function UserAppConfigContentComponent_Conditional_3_Conditional_26_Template(rf, ctx) { if (rf & 1) {
131
+ const _r5 = i0.ɵɵgetCurrentView();
132
+ i0.ɵɵelementStart(0, "div", 35);
133
+ i0.ɵɵlistener("dragover", function UserAppConfigContentComponent_Conditional_3_Conditional_26_Template_div_dragover_0_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.OnDragOver($event)); })("drop", function UserAppConfigContentComponent_Conditional_3_Conditional_26_Template_div_drop_0_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.OnDrop($event)); });
134
+ i0.ɵɵrepeaterCreate(1, UserAppConfigContentComponent_Conditional_3_Conditional_26_For_2_Template, 17, 13, "div", 36, _forTrack0);
135
+ i0.ɵɵelementEnd();
136
+ } if (rf & 2) {
137
+ const ctx_r0 = i0.ɵɵnextContext(2);
138
+ i0.ɵɵadvance();
139
+ i0.ɵɵrepeater(ctx_r0.ActiveApps);
140
+ } }
141
+ function UserAppConfigContentComponent_Conditional_3_Conditional_29_For_3_Conditional_0_Template(rf, ctx) { if (rf & 1) {
142
+ i0.ɵɵelement(0, "div", 49);
143
+ } }
144
+ function UserAppConfigContentComponent_Conditional_3_Conditional_29_For_3_Conditional_10_Template(rf, ctx) { if (rf & 1) {
145
+ i0.ɵɵelement(0, "div", 49);
146
+ } }
147
+ function UserAppConfigContentComponent_Conditional_3_Conditional_29_For_3_Template(rf, ctx) { if (rf & 1) {
148
+ const _r10 = i0.ɵɵgetCurrentView();
149
+ i0.ɵɵconditionalCreate(0, UserAppConfigContentComponent_Conditional_3_Conditional_29_For_3_Conditional_0_Template, 1, 0, "div", 49);
150
+ i0.ɵɵelementStart(1, "div", 50)(2, "div", 51);
151
+ i0.ɵɵlistener("touchstart", function UserAppConfigContentComponent_Conditional_3_Conditional_29_For_3_Template_div_touchstart_2_listener($event) { const ɵ$index_126_r11 = i0.ɵɵrestoreView(_r10).$index; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.OnTouchDragStart($event, ɵ$index_126_r11)); });
152
+ i0.ɵɵelement(3, "i", 40);
153
+ i0.ɵɵelementEnd();
154
+ i0.ɵɵelementStart(4, "div", 28);
155
+ i0.ɵɵelement(5, "i", 29);
156
+ i0.ɵɵelementEnd();
157
+ i0.ɵɵelementStart(6, "span", 52);
158
+ i0.ɵɵtext(7);
159
+ i0.ɵɵelementEnd();
160
+ i0.ɵɵelementStart(8, "button", 53);
161
+ i0.ɵɵlistener("click", function UserAppConfigContentComponent_Conditional_3_Conditional_29_For_3_Template_button_click_8_listener() { const item_r12 = i0.ɵɵrestoreView(_r10).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.ToggleApp(item_r12)); });
162
+ i0.ɵɵelement(9, "i", 54);
163
+ i0.ɵɵelementEnd()();
164
+ i0.ɵɵconditionalCreate(10, UserAppConfigContentComponent_Conditional_3_Conditional_29_For_3_Conditional_10_Template, 1, 0, "div", 49);
165
+ } if (rf & 2) {
166
+ const item_r12 = ctx.$implicit;
167
+ const ɵ$index_126_r11 = ctx.$index;
168
+ const ctx_r0 = i0.ɵɵnextContext(3);
169
+ i0.ɵɵconditional(ctx_r0.TouchDragActive && ctx_r0.TouchDropIndex === ɵ$index_126_r11 && ctx_r0.TouchDragIndex > ɵ$index_126_r11 ? 0 : -1);
170
+ i0.ɵɵadvance();
171
+ i0.ɵɵclassProp("touch-ghost", ctx_r0.TouchDragActive && ctx_r0.TouchDragIndex === ɵ$index_126_r11);
172
+ i0.ɵɵadvance(3);
173
+ i0.ɵɵstyleProp("--app-color", item_r12.app.GetColor());
174
+ i0.ɵɵadvance();
175
+ i0.ɵɵclassMap(item_r12.app.Icon || "fa-solid fa-cube");
176
+ i0.ɵɵadvance(2);
177
+ i0.ɵɵtextInterpolate(item_r12.app.Name);
178
+ i0.ɵɵadvance();
179
+ i0.ɵɵariaProperty("aria-label", i0.ɵɵinterpolate1("Remove ", item_r12.app.Name));
180
+ i0.ɵɵadvance(2);
181
+ i0.ɵɵconditional(ctx_r0.TouchDragActive && ctx_r0.TouchDropIndex === ɵ$index_126_r11 && ctx_r0.TouchDragIndex < ɵ$index_126_r11 ? 10 : -1);
182
+ } }
183
+ function UserAppConfigContentComponent_Conditional_3_Conditional_29_Conditional_4_Template(rf, ctx) { if (rf & 1) {
184
+ i0.ɵɵelement(0, "div", 49);
185
+ } }
186
+ function UserAppConfigContentComponent_Conditional_3_Conditional_29_Template(rf, ctx) { if (rf & 1) {
187
+ i0.ɵɵelementStart(0, "div", 48);
188
+ i0.ɵɵtext(1, "Your Apps");
189
+ i0.ɵɵelementEnd();
190
+ i0.ɵɵrepeaterCreate(2, UserAppConfigContentComponent_Conditional_3_Conditional_29_For_3_Template, 11, 11, null, null, _forTrack0);
191
+ i0.ɵɵconditionalCreate(4, UserAppConfigContentComponent_Conditional_3_Conditional_29_Conditional_4_Template, 1, 0, "div", 49);
192
+ } if (rf & 2) {
193
+ const ctx_r0 = i0.ɵɵnextContext(2);
194
+ i0.ɵɵadvance(2);
195
+ i0.ɵɵrepeater(ctx_r0.ActiveApps);
196
+ i0.ɵɵadvance(2);
197
+ i0.ɵɵconditional(ctx_r0.TouchDragActive && ctx_r0.TouchDropIndex === ctx_r0.ActiveApps.length - 1 && ctx_r0.TouchDragIndex < ctx_r0.TouchDropIndex ? 4 : -1);
198
+ } }
199
+ function UserAppConfigContentComponent_Conditional_3_Conditional_30_For_3_Template(rf, ctx) { if (rf & 1) {
200
+ const _r13 = i0.ɵɵgetCurrentView();
201
+ i0.ɵɵelementStart(0, "div", 55)(1, "div", 28);
202
+ i0.ɵɵelement(2, "i", 29);
203
+ i0.ɵɵelementEnd();
204
+ i0.ɵɵelementStart(3, "span", 52);
205
+ i0.ɵɵtext(4);
206
+ i0.ɵɵelementEnd();
207
+ i0.ɵɵelementStart(5, "button", 56);
208
+ i0.ɵɵlistener("click", function UserAppConfigContentComponent_Conditional_3_Conditional_30_For_3_Template_button_click_5_listener() { const item_r14 = i0.ɵɵrestoreView(_r13).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.ToggleApp(item_r14)); });
209
+ i0.ɵɵelement(6, "i", 57);
210
+ i0.ɵɵelementEnd()();
211
+ } if (rf & 2) {
212
+ const item_r14 = ctx.$implicit;
213
+ i0.ɵɵadvance();
214
+ i0.ɵɵstyleProp("--app-color", item_r14.app.GetColor());
215
+ i0.ɵɵadvance();
216
+ i0.ɵɵclassMap(item_r14.app.Icon || "fa-solid fa-cube");
217
+ i0.ɵɵadvance(2);
218
+ i0.ɵɵtextInterpolate(item_r14.app.Name);
219
+ i0.ɵɵadvance();
220
+ i0.ɵɵariaProperty("aria-label", i0.ɵɵinterpolate1("Add ", item_r14.app.Name));
221
+ } }
222
+ function UserAppConfigContentComponent_Conditional_3_Conditional_30_Template(rf, ctx) { if (rf & 1) {
223
+ i0.ɵɵelementStart(0, "div", 48);
224
+ i0.ɵɵtext(1, "Available");
225
+ i0.ɵɵelementEnd();
226
+ i0.ɵɵrepeaterCreate(2, UserAppConfigContentComponent_Conditional_3_Conditional_30_For_3_Template, 7, 7, "div", 55, _forTrack0);
227
+ } if (rf & 2) {
228
+ const ctx_r0 = i0.ɵɵnextContext(2);
229
+ i0.ɵɵadvance(2);
230
+ i0.ɵɵrepeater(ctx_r0.AvailableApps);
231
+ } }
232
+ function UserAppConfigContentComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
233
+ const _r2 = i0.ɵɵgetCurrentView();
234
+ i0.ɵɵelementStart(0, "div", 3)(1, "p", 11);
235
+ i0.ɵɵtext(2, " Select which applications appear in your app switcher and arrange them in your preferred order. Double-click to quickly add or remove. Drag and drop to reorder, or use the arrow buttons. ");
236
+ i0.ɵɵelementEnd();
237
+ i0.ɵɵelementStart(3, "div", 12)(4, "div", 13)(5, "div", 14);
238
+ i0.ɵɵlistener("click", function UserAppConfigContentComponent_Conditional_3_Template_div_click_5_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.AvailablePanelCollapsed = !ctx_r0.AvailablePanelCollapsed); });
239
+ i0.ɵɵelement(6, "i", 15);
240
+ i0.ɵɵelementStart(7, "h3");
241
+ i0.ɵɵtext(8, "Available Applications");
242
+ i0.ɵɵelementEnd();
243
+ i0.ɵɵelementStart(9, "span", 16);
244
+ i0.ɵɵtext(10);
245
+ i0.ɵɵelementEnd();
246
+ i0.ɵɵelement(11, "i", 17);
247
+ i0.ɵɵelementEnd();
248
+ i0.ɵɵelementStart(12, "div", 18);
249
+ i0.ɵɵconditionalCreate(13, UserAppConfigContentComponent_Conditional_3_Conditional_13_Template, 1, 0, "mj-empty-state", 19);
250
+ i0.ɵɵrepeaterCreate(14, UserAppConfigContentComponent_Conditional_3_For_15_Template, 10, 7, "div", 20, _forTrack0);
251
+ i0.ɵɵelementEnd()();
252
+ i0.ɵɵelementStart(16, "div", 21)(17, "div", 14);
253
+ i0.ɵɵlistener("click", function UserAppConfigContentComponent_Conditional_3_Template_div_click_17_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.SelectedPanelCollapsed = !ctx_r0.SelectedPanelCollapsed); });
254
+ i0.ɵɵelement(18, "i", 22);
255
+ i0.ɵɵelementStart(19, "h3");
256
+ i0.ɵɵtext(20, "Your Applications");
257
+ i0.ɵɵelementEnd();
258
+ i0.ɵɵelementStart(21, "span", 16);
259
+ i0.ɵɵtext(22);
260
+ i0.ɵɵelementEnd();
261
+ i0.ɵɵelement(23, "i", 17);
262
+ i0.ɵɵelementEnd();
263
+ i0.ɵɵelementStart(24, "div", 18);
264
+ i0.ɵɵconditionalCreate(25, UserAppConfigContentComponent_Conditional_3_Conditional_25_Template, 1, 0, "mj-empty-state", 23);
265
+ i0.ɵɵconditionalCreate(26, UserAppConfigContentComponent_Conditional_3_Conditional_26_Template, 3, 0, "div", 24);
266
+ i0.ɵɵelementEnd()()();
267
+ i0.ɵɵelementStart(27, "div", 25);
268
+ i0.ɵɵlistener("touchmove", function UserAppConfigContentComponent_Conditional_3_Template_div_touchmove_27_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnTouchDragMove($event)); })("touchend", function UserAppConfigContentComponent_Conditional_3_Template_div_touchend_27_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnTouchDragEnd()); })("touchcancel", function UserAppConfigContentComponent_Conditional_3_Template_div_touchcancel_27_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnTouchDragEnd()); });
269
+ i0.ɵɵelementStart(28, "div", 26);
270
+ i0.ɵɵconditionalCreate(29, UserAppConfigContentComponent_Conditional_3_Conditional_29_Template, 5, 1);
271
+ i0.ɵɵconditionalCreate(30, UserAppConfigContentComponent_Conditional_3_Conditional_30_Template, 4, 0);
272
+ i0.ɵɵelementEnd()()();
273
+ } if (rf & 2) {
274
+ const ctx_r0 = i0.ɵɵnextContext();
275
+ i0.ɵɵadvance(4);
276
+ i0.ɵɵclassProp("collapsed", ctx_r0.AvailablePanelCollapsed);
277
+ i0.ɵɵadvance(6);
278
+ i0.ɵɵtextInterpolate(ctx_r0.AvailableApps.length);
279
+ i0.ɵɵadvance();
280
+ i0.ɵɵclassProp("fa-chevron-down", ctx_r0.AvailablePanelCollapsed)("fa-chevron-up", !ctx_r0.AvailablePanelCollapsed);
281
+ i0.ɵɵadvance(2);
282
+ i0.ɵɵconditional(ctx_r0.AvailableApps.length === 0 ? 13 : -1);
283
+ i0.ɵɵadvance();
284
+ i0.ɵɵrepeater(ctx_r0.AvailableApps);
285
+ i0.ɵɵadvance(2);
286
+ i0.ɵɵclassProp("collapsed", ctx_r0.SelectedPanelCollapsed);
287
+ i0.ɵɵadvance(6);
288
+ i0.ɵɵtextInterpolate(ctx_r0.ActiveApps.length);
289
+ i0.ɵɵadvance();
290
+ i0.ɵɵclassProp("fa-chevron-down", ctx_r0.SelectedPanelCollapsed)("fa-chevron-up", !ctx_r0.SelectedPanelCollapsed);
291
+ i0.ɵɵadvance(2);
292
+ i0.ɵɵconditional(ctx_r0.ActiveApps.length === 0 ? 25 : -1);
293
+ i0.ɵɵadvance();
294
+ i0.ɵɵconditional(ctx_r0.ActiveApps.length > 0 ? 26 : -1);
295
+ i0.ɵɵadvance(3);
296
+ i0.ɵɵconditional(ctx_r0.ActiveApps.length > 0 ? 29 : -1);
297
+ i0.ɵɵadvance();
298
+ i0.ɵɵconditional(ctx_r0.AvailableApps.length > 0 ? 30 : -1);
299
+ } }
300
+ function UserAppConfigContentComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
301
+ const _r15 = i0.ɵɵgetCurrentView();
302
+ i0.ɵɵelementStart(0, "button", 9);
303
+ i0.ɵɵlistener("click", function UserAppConfigContentComponent_Conditional_6_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r15); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.Reset()); });
304
+ i0.ɵɵelement(1, "i", 58);
305
+ i0.ɵɵtext(2, " Reset Changes ");
306
+ i0.ɵɵelementEnd();
307
+ } if (rf & 2) {
308
+ const ctx_r0 = i0.ɵɵnextContext();
309
+ i0.ɵɵproperty("disabled", ctx_r0.IsLoading || ctx_r0.IsSaving);
310
+ } }
311
+ function UserAppConfigContentComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
312
+ i0.ɵɵelement(0, "mj-loading", 59);
313
+ i0.ɵɵelementStart(1, "span");
314
+ i0.ɵɵtext(2, "Saving...");
315
+ i0.ɵɵelementEnd();
316
+ } if (rf & 2) {
317
+ i0.ɵɵproperty("showText", false);
318
+ } }
319
+ function UserAppConfigContentComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
320
+ i0.ɵɵelement(0, "i", 60);
321
+ i0.ɵɵelementStart(1, "span");
322
+ i0.ɵɵtext(2, "Save Changes");
323
+ i0.ɵɵelementEnd();
324
+ } }
325
+ /**
326
+ * Chrome-free app-configuration content: select which applications appear in
327
+ * the app switcher and arrange their order. Owns all list/reorder/persistence
328
+ * logic (including its own Reset / Save / Cancel footer) but NO overlay —
329
+ * hosts embed it wherever it should live:
330
+ * - `UserAppConfigComponent` wraps it in the full-screen modal (Home dashboard)
331
+ * - the shell's app launcher embeds it as an in-panel view swap
332
+ *
333
+ * Loads on init (hosts create it fresh under an `@if`), saves in a batch on
334
+ * the Save button, and signals the host via `Saved` / `Cancelled` — the host
335
+ * decides what "done" means (close the dialog, swap the view back, etc.).
336
+ */
337
+ export class UserAppConfigContentComponent extends BaseAngularComponent {
338
+ appManager = inject(ApplicationManager);
339
+ sharedService = inject(SharedService);
340
+ cdr = inject(ChangeDetectorRef);
341
+ ngZone = inject(NgZone);
342
+ el = inject(ElementRef);
343
+ /** Emitted once after a successful batch save */
344
+ Saved = new EventEmitter();
345
+ /** Emitted when the user cancels (or saves with nothing to save) */
346
+ Cancelled = new EventEmitter();
347
+ /** Every system app the user is authorized for, joined with their per-user config */
348
+ AllApps = [];
349
+ /** Apps the user has enabled, in their configured Sequence order */
350
+ ActiveApps = [];
351
+ /** Apps not yet enabled, alphabetical */
352
+ AvailableApps = [];
353
+ /** True while the initial configuration load is in flight */
354
+ IsLoading = false;
355
+ /** True while the batch save is in flight (disables the footer buttons) */
356
+ IsSaving = false;
357
+ /** User-facing load/save failure message; empty when healthy */
358
+ ErrorMessage = '';
359
+ /** Collapse state for the Available-apps panel (mobile accordion) */
360
+ AvailablePanelCollapsed = false;
361
+ /** Collapse state for the Selected-apps panel (mobile accordion) */
362
+ SelectedPanelCollapsed = false;
363
+ /** Item currently being dragged for reorder, null when idle */
364
+ DraggedItem = null;
365
+ /** Source index of the in-flight drag within ActiveApps */
366
+ DraggedIndex = -1;
367
+ /** Index the drag is currently hovering (drop indicator position) */
368
+ DropTargetIndex = -1;
369
+ async ngOnInit() {
370
+ await this.loadConfiguration();
371
+ }
372
+ /**
373
+ * Adds an app to the user's active list
374
+ */
375
+ AddApp(item) {
376
+ item.isActive = true;
377
+ item.sequence = this.ActiveApps.length;
378
+ item.isDirty = true;
379
+ this.refreshAppLists();
380
+ }
381
+ /**
382
+ * Removes an app from the user's active list
383
+ */
384
+ RemoveApp(item) {
385
+ item.isActive = false;
386
+ item.sequence = 999;
387
+ item.isDirty = true;
388
+ this.refreshAppLists();
389
+ this.resequenceActiveApps();
390
+ }
391
+ /**
392
+ * Toggles an app between active and inactive
393
+ */
394
+ ToggleApp(item) {
395
+ if (item.isActive) {
396
+ this.RemoveApp(item);
397
+ }
398
+ else {
399
+ this.AddApp(item);
400
+ }
401
+ }
402
+ /**
403
+ * Moves an app up in the order. Positional (splice + renumber), NOT a sequence-value
404
+ * swap: rows can carry duplicate Sequence values (issue #3027 — e.g. a re-enabled app
405
+ * kept a stale value), and swapping two equal values is a silent no-op. Reordering by
406
+ * position then renumbering 0..n-1 always works and heals duplicates as a side effect.
407
+ */
408
+ MoveUp(item) {
409
+ const index = this.ActiveApps.indexOf(item);
410
+ if (index > 0) {
411
+ this.moveActiveApp(index, index - 1);
412
+ }
413
+ }
414
+ /**
415
+ * Moves an app down in the order. Positional — see {@link MoveUp} for why.
416
+ */
417
+ MoveDown(item) {
418
+ const index = this.ActiveApps.indexOf(item);
419
+ if (index >= 0 && index < this.ActiveApps.length - 1) {
420
+ this.moveActiveApp(index, index + 1);
421
+ }
422
+ }
423
+ moveActiveApp(fromIndex, toIndex) {
424
+ this.ActiveApps = moveAndResequence(this.ActiveApps, fromIndex, toIndex);
425
+ }
426
+ /**
427
+ * Checks if there are any unsaved changes
428
+ */
429
+ HasChanges() {
430
+ return this.AllApps.some(item => item.isDirty);
431
+ }
432
+ /** Cancel button — host decides what dismissal means */
433
+ Cancel() {
434
+ this.Cancelled.emit();
435
+ }
436
+ /**
437
+ * Saves the user's app configuration
438
+ */
439
+ async Save() {
440
+ if (!this.HasChanges()) {
441
+ this.Cancelled.emit();
442
+ return;
443
+ }
444
+ this.IsSaving = true;
445
+ this.ErrorMessage = '';
446
+ try {
447
+ // Self-heal: renumber active apps 0..n-1 before persisting. Existing rows can
448
+ // carry duplicate Sequence values from older data (issue #3027); renumbering in
449
+ // display order marks any corrected rows dirty so this save fixes them for good.
450
+ this.resequenceActiveApps();
451
+ const md = this.ProviderToUse;
452
+ for (const item of this.AllApps) {
453
+ if (!item.isDirty)
454
+ continue;
455
+ if (item.userAppId) {
456
+ await this.updateUserApplication(md, item);
457
+ }
458
+ else if (item.isActive) {
459
+ await this.createUserApplication(md, item);
460
+ }
461
+ }
462
+ // Each userApp.Save() above fires a BaseEntity 'save' event, which UserInfoEngine
463
+ // catches via HandleIndividualBaseEntityEvent → debounced refresh → NotifyDataChange.
464
+ // ApplicationManager.subscribeToEngineChanges then propagates to the shell app
465
+ // switcher. No explicit reload needed here — and avoiding it sidesteps NG0100
466
+ // from observable emissions interleaving with this method's save loop.
467
+ LogStatusEx({
468
+ message: 'User app configuration saved',
469
+ verboseOnly: true
470
+ });
471
+ this.sharedService.CreateSimpleNotification('App configuration saved successfully!', 'success', 3000);
472
+ this.Saved.emit();
473
+ // Backstop for a dropped/coalesced debounced engine event: force a reconcile from the source
474
+ // of truth so the app switcher can't be left "one save behind". DEFERRED past the current CD
475
+ // cycle — a synchronous reload here previously triggered NG0100 (commits e2059e6114 /
476
+ // 7bf8be7dc6). No-op cost when the event chain already delivered. (Ported from bug F1's fix
477
+ // to the pre-split monolith, PR #3289.)
478
+ Promise.resolve().then(() => this.appManager.ReloadUserApplications());
479
+ }
480
+ catch (error) {
481
+ this.ErrorMessage = 'Failed to save configuration. Please try again.';
482
+ LogError('Error saving app configuration:', undefined, error instanceof Error ? error.message : String(error));
483
+ // A partial-save failure can leave in-memory state half-committed (some items saved+clean,
484
+ // others still dirty) and divergent from the DB. Reconcile from the source of truth so
485
+ // recovery doesn't require a browser refresh — deferred, for the same NG0100 reason.
486
+ Promise.resolve().then(() => this.appManager.ReloadUserApplications());
487
+ }
488
+ finally {
489
+ this.ngZone.run(() => {
490
+ this.IsSaving = false;
491
+ this.cdr.detectChanges();
492
+ });
493
+ }
494
+ }
495
+ /**
496
+ * Resets all changes and reloads the configuration
497
+ */
498
+ async Reset() {
499
+ await this.loadConfiguration();
500
+ }
501
+ // ---------------------------------------------------------------------------
502
+ // Drag-and-drop handlers (desktop only)
503
+ // ---------------------------------------------------------------------------
504
+ /** Begin an HTML5 drag of an active-app row */
505
+ OnDragStart(event, item, index) {
506
+ this.DraggedItem = item;
507
+ this.DraggedIndex = index;
508
+ if (event.dataTransfer) {
509
+ event.dataTransfer.effectAllowed = 'move';
510
+ event.dataTransfer.setData('text/plain', index.toString());
511
+ }
512
+ }
513
+ /** Allow dropping while a row drag passes over the list */
514
+ OnDragOver(event) {
515
+ event.preventDefault();
516
+ if (event.dataTransfer) {
517
+ event.dataTransfer.dropEffect = 'move';
518
+ }
519
+ }
520
+ /** Track the row index the drag is hovering (drop indicator) */
521
+ OnDragEnter(event, index) {
522
+ event.preventDefault();
523
+ this.DropTargetIndex = index;
524
+ }
525
+ /** Clear all drag state (fires on drop or drag cancel) */
526
+ OnDragEnd() {
527
+ this.DraggedItem = null;
528
+ this.DraggedIndex = -1;
529
+ this.DropTargetIndex = -1;
530
+ }
531
+ /** Reorder ActiveApps to the hovered position and renumber sequences */
532
+ OnDrop(event) {
533
+ event.preventDefault();
534
+ if (this.DraggedIndex >= 0 && this.DropTargetIndex >= 0 && this.DraggedIndex !== this.DropTargetIndex) {
535
+ const [movedItem] = this.ActiveApps.splice(this.DraggedIndex, 1);
536
+ this.ActiveApps.splice(this.DropTargetIndex, 0, movedItem);
537
+ this.resequenceActiveApps();
538
+ this.cdr.detectChanges();
539
+ }
540
+ this.OnDragEnd();
541
+ }
542
+ // ---------------------------------------------------------------------------
543
+ // Touch drag-and-drop handlers (mobile)
544
+ // ---------------------------------------------------------------------------
545
+ /** Source index of the in-flight touch drag (-1 when idle) */
546
+ TouchDragIndex = -1;
547
+ /** Row index the touch drag would drop at */
548
+ TouchDropIndex = -1;
549
+ /** True while a touch drag is in progress */
550
+ TouchDragActive = false;
551
+ touchStartY = 0;
552
+ touchCurrentY = 0;
553
+ touchRowHeight = 0;
554
+ touchDragElement = null;
555
+ touchScrollContainer = null;
556
+ /** Begin a touch drag of a mobile app row (long-press handle) */
557
+ OnTouchDragStart(event, index) {
558
+ const touch = event.touches[0];
559
+ const row = event.target.closest('.mobile-app-row');
560
+ if (!row)
561
+ return;
562
+ event.preventDefault();
563
+ this.TouchDragIndex = index;
564
+ this.TouchDropIndex = index;
565
+ this.TouchDragActive = true;
566
+ this.touchStartY = touch.clientY;
567
+ this.touchCurrentY = touch.clientY;
568
+ this.touchDragElement = row;
569
+ this.touchRowHeight = row.offsetHeight;
570
+ this.touchScrollContainer = this.el.nativeElement.querySelector('.mobile-list');
571
+ row.classList.add('touch-dragging');
572
+ }
573
+ /** Follow the finger: translate the row, track drop target, auto-scroll near edges */
574
+ OnTouchDragMove(event) {
575
+ if (!this.TouchDragActive || !this.touchDragElement)
576
+ return;
577
+ event.preventDefault();
578
+ const touch = event.touches[0];
579
+ this.touchCurrentY = touch.clientY;
580
+ const deltaY = this.touchCurrentY - this.touchStartY;
581
+ this.touchDragElement.style.transform = `translateY(${deltaY}px)`;
582
+ this.touchDragElement.style.zIndex = '100';
583
+ this.updateTouchDropTarget(deltaY);
584
+ this.autoScrollIfNeeded();
585
+ }
586
+ /** Commit the touch reorder (if moved) and reset touch-drag state */
587
+ OnTouchDragEnd() {
588
+ if (!this.TouchDragActive)
589
+ return;
590
+ if (this.touchDragElement) {
591
+ this.touchDragElement.style.transform = '';
592
+ this.touchDragElement.style.zIndex = '';
593
+ this.touchDragElement.classList.remove('touch-dragging');
594
+ }
595
+ if (this.TouchDragIndex >= 0 && this.TouchDropIndex >= 0 && this.TouchDragIndex !== this.TouchDropIndex) {
596
+ const [movedItem] = this.ActiveApps.splice(this.TouchDragIndex, 1);
597
+ this.ActiveApps.splice(this.TouchDropIndex, 0, movedItem);
598
+ this.resequenceActiveApps();
599
+ }
600
+ this.resetTouchDragState();
601
+ this.cdr.detectChanges();
602
+ }
603
+ updateTouchDropTarget(deltaY) {
604
+ const rowsToMove = Math.round(deltaY / this.touchRowHeight);
605
+ const newIndex = Math.max(0, Math.min(this.ActiveApps.length - 1, this.TouchDragIndex + rowsToMove));
606
+ this.TouchDropIndex = newIndex;
607
+ }
608
+ autoScrollIfNeeded() {
609
+ if (!this.touchScrollContainer)
610
+ return;
611
+ const rect = this.touchScrollContainer.getBoundingClientRect();
612
+ const edgeZone = 40;
613
+ if (this.touchCurrentY < rect.top + edgeZone) {
614
+ this.touchScrollContainer.scrollTop -= 8;
615
+ }
616
+ else if (this.touchCurrentY > rect.bottom - edgeZone) {
617
+ this.touchScrollContainer.scrollTop += 8;
618
+ }
619
+ }
620
+ resetTouchDragState() {
621
+ this.TouchDragIndex = -1;
622
+ this.TouchDropIndex = -1;
623
+ this.TouchDragActive = false;
624
+ this.touchDragElement = null;
625
+ this.touchScrollContainer = null;
626
+ }
627
+ // ---------------------------------------------------------------------------
628
+ // Private helpers
629
+ // ---------------------------------------------------------------------------
630
+ async loadConfiguration() {
631
+ this.IsLoading = true;
632
+ this.ErrorMessage = '';
633
+ try {
634
+ // Read directly from UserInfoEngine's cache. The engine already loaded
635
+ // and maintains MJ: User Applications via its event-driven refresh — no
636
+ // need to re-query the DB on every open. EnsureLoaded is idempotent
637
+ // and instant when the engine is already loaded (the typical case here,
638
+ // since UserInfoEngine fires at startup).
639
+ const provider = this.ProviderToUse;
640
+ const engine = provider
641
+ ? UserInfoEngine.GetProviderInstance(provider, UserInfoEngine)
642
+ : UserInfoEngine.Instance;
643
+ await engine.EnsureLoaded();
644
+ const systemApps = this.appManager.GetAuthorizedSystemApps();
645
+ const userApps = engine.UserApplications;
646
+ this.AllApps = this.buildAppConfigItems(systemApps, userApps);
647
+ this.refreshAppLists();
648
+ }
649
+ catch (error) {
650
+ this.ErrorMessage = 'Failed to load app configuration. Please try again.';
651
+ LogError('Error loading app configuration:', undefined, error instanceof Error ? error.message : String(error));
652
+ }
653
+ finally {
654
+ this.ngZone.run(() => {
655
+ this.IsLoading = false;
656
+ this.cdr.detectChanges();
657
+ });
658
+ }
659
+ }
660
+ buildAppConfigItems(systemApps, userApps) {
661
+ return systemApps.map(app => {
662
+ const userApp = userApps.find(ua => UUIDsEqual(ua.ApplicationID, app.ID));
663
+ return {
664
+ app,
665
+ userAppId: userApp?.ID ?? null,
666
+ sequence: userApp?.Sequence ?? 999,
667
+ isActive: userApp?.IsActive ?? false,
668
+ isDirty: false
669
+ };
670
+ });
671
+ }
672
+ refreshAppLists() {
673
+ this.ActiveApps = this.AllApps
674
+ .filter(item => item.isActive)
675
+ .sort((a, b) => a.sequence - b.sequence);
676
+ this.AvailableApps = this.AllApps
677
+ .filter(item => !item.isActive)
678
+ .sort((a, b) => a.app.Name.localeCompare(b.app.Name));
679
+ }
680
+ resequenceActiveApps() {
681
+ resequenceItems(this.ActiveApps);
682
+ }
683
+ async updateUserApplication(md, item) {
684
+ const userApp = await md.GetEntityObject('MJ: User Applications', md.CurrentUser);
685
+ await userApp.Load(item.userAppId);
686
+ userApp.Sequence = item.sequence;
687
+ userApp.IsActive = item.isActive;
688
+ const saved = await userApp.Save();
689
+ if (!saved) {
690
+ throw new Error(`Failed to update UserApplication for ${item.app.Name}: ${userApp.LatestResult?.CompleteMessage ?? 'unknown error'}`);
691
+ }
692
+ item.isDirty = false;
693
+ LogStatus(`Updated UserApplication for ${item.app.Name}: sequence=${item.sequence}, isActive=${item.isActive}`);
694
+ }
695
+ async createUserApplication(md, item) {
696
+ const userApp = await md.GetEntityObject('MJ: User Applications', md.CurrentUser);
697
+ userApp.NewRecord();
698
+ userApp.UserID = md.CurrentUser.ID;
699
+ userApp.ApplicationID = item.app.ID;
700
+ userApp.Sequence = item.sequence;
701
+ userApp.IsActive = item.isActive;
702
+ const saved = await userApp.Save();
703
+ if (!saved) {
704
+ throw new Error(`Failed to create UserApplication for ${item.app.Name}: ${userApp.LatestResult?.CompleteMessage ?? 'unknown error'}`);
705
+ }
706
+ item.userAppId = userApp.ID;
707
+ item.isDirty = false;
708
+ LogStatus(`Created UserApplication for ${item.app.Name}: sequence=${item.sequence}`);
709
+ }
710
+ static ɵfac = /*@__PURE__*/ (() => { let ɵUserAppConfigContentComponent_BaseFactory; return function UserAppConfigContentComponent_Factory(__ngFactoryType__) { return (ɵUserAppConfigContentComponent_BaseFactory || (ɵUserAppConfigContentComponent_BaseFactory = i0.ɵɵgetInheritedFactory(UserAppConfigContentComponent)))(__ngFactoryType__ || UserAppConfigContentComponent); }; })();
711
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: UserAppConfigContentComponent, selectors: [["mj-user-app-config-content"]], outputs: { Saved: "Saved", Cancelled: "Cancelled" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 13, vars: 7, consts: [[1, "app-config-container"], ["role", "status", "aria-live", "polite", 1, "loading-container"], ["Variant", "error"], [1, "config-content"], [1, "dialog-footer"], [1, "footer-left"], ["mjButton", "", 3, "disabled"], [1, "footer-right"], ["mjButton", "", "variant", "primary", 3, "click", "disabled"], ["mjButton", "", 3, "click", "disabled"], ["text", "Loading your app configuration...", "size", "medium"], [1, "config-description"], [1, "panels-container", "desktop-only"], [1, "panel", "available-apps-panel"], [1, "panel-header", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-layer-group"], [1, "badge"], ["aria-hidden", "true", 1, "collapse-toggle", "fa-solid"], [1, "panel-content"], ["Size", "compact", "Icon", "fa-solid fa-check-double", "Title", "All applications added!"], [1, "available-app-item"], [1, "panel", "selected-apps-panel"], ["aria-hidden", "true", 1, "fa-solid", "fa-check-circle"], ["Size", "compact", "Icon", "fa-solid fa-inbox", "Title", "No applications selected", "Message", "Add apps from the available list"], [1, "sortable-list"], [1, "mobile-view", 3, "touchmove", "touchend", "touchcancel"], [1, "mobile-list"], [1, "available-app-item", 3, "dblclick"], [1, "app-icon"], ["aria-hidden", "true"], [1, "app-info"], [1, "app-name"], [1, "app-description"], ["mjButton", "", "variant", "outline", "size", "sm", "title", "Add to your apps", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-plus"], [1, "sortable-list", 3, "dragover", "drop"], ["draggable", "true", 1, "app-item", 3, "dragging"], ["draggable", "true", 1, "app-item", 3, "dragstart", "dragend", "dragenter", "dblclick"], [1, "app-item-content"], [1, "drag-handle"], ["aria-hidden", "true", 1, "fa-solid", "fa-grip-vertical"], [1, "app-actions"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Move up", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-chevron-up"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Move down", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-chevron-down"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Remove from your apps", 1, "remove-btn", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-xmark"], [1, "mobile-section-label"], [1, "mobile-drop-indicator"], [1, "mobile-app-row"], ["aria-label", "Drag to reorder", 1, "mobile-drag-handle", 3, "touchstart"], [1, "mobile-app-name"], [1, "mobile-toggle", "on", 3, "click", "aria-label"], ["aria-hidden", "true", 1, "fa-solid", "fa-toggle-on"], [1, "mobile-app-row", "inactive"], [1, "mobile-toggle", "off", 3, "click", "aria-label"], ["aria-hidden", "true", 1, "fa-solid", "fa-toggle-off"], ["aria-hidden", "true", 1, "fa-solid", "fa-rotate-left"], ["size", "small", 3, "showText"], ["aria-hidden", "true", 1, "fa-solid", "fa-check"]], template: function UserAppConfigContentComponent_Template(rf, ctx) { if (rf & 1) {
712
+ i0.ɵɵelementStart(0, "div", 0);
713
+ i0.ɵɵconditionalCreate(1, UserAppConfigContentComponent_Conditional_1_Template, 2, 0, "div", 1);
714
+ i0.ɵɵconditionalCreate(2, UserAppConfigContentComponent_Conditional_2_Template, 2, 1, "mj-alert", 2);
715
+ i0.ɵɵconditionalCreate(3, UserAppConfigContentComponent_Conditional_3_Template, 31, 19, "div", 3);
716
+ i0.ɵɵelementStart(4, "div", 4)(5, "div", 5);
717
+ i0.ɵɵconditionalCreate(6, UserAppConfigContentComponent_Conditional_6_Template, 3, 1, "button", 6);
718
+ i0.ɵɵelementEnd();
719
+ i0.ɵɵelementStart(7, "div", 7)(8, "button", 8);
720
+ i0.ɵɵlistener("click", function UserAppConfigContentComponent_Template_button_click_8_listener() { return ctx.Save(); });
721
+ i0.ɵɵconditionalCreate(9, UserAppConfigContentComponent_Conditional_9_Template, 3, 1)(10, UserAppConfigContentComponent_Conditional_10_Template, 3, 0);
722
+ i0.ɵɵelementEnd();
723
+ i0.ɵɵelementStart(11, "button", 9);
724
+ i0.ɵɵlistener("click", function UserAppConfigContentComponent_Template_button_click_11_listener() { return ctx.Cancel(); });
725
+ i0.ɵɵtext(12, " Cancel ");
726
+ i0.ɵɵelementEnd()()()();
727
+ } if (rf & 2) {
728
+ i0.ɵɵadvance();
729
+ i0.ɵɵconditional(ctx.IsLoading ? 1 : -1);
730
+ i0.ɵɵadvance();
731
+ i0.ɵɵconditional(ctx.ErrorMessage ? 2 : -1);
732
+ i0.ɵɵadvance();
733
+ i0.ɵɵconditional(!ctx.IsLoading ? 3 : -1);
734
+ i0.ɵɵadvance(3);
735
+ i0.ɵɵconditional(ctx.HasChanges() ? 6 : -1);
736
+ i0.ɵɵadvance(2);
737
+ i0.ɵɵproperty("disabled", ctx.IsLoading || ctx.IsSaving || !ctx.HasChanges());
738
+ i0.ɵɵadvance();
739
+ i0.ɵɵconditional(ctx.IsSaving ? 9 : 10);
740
+ i0.ɵɵadvance(2);
741
+ i0.ɵɵproperty("disabled", ctx.IsSaving);
742
+ } }, dependencies: [i1.LoadingComponent, i2.MJButtonDirective, i2.MJEmptyStateComponent, i2.MJAlertComponent], styles: ["\n\n\n\n\n\n\n[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-height: 0;\n}\n\n\n\n.app-config-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n background: var(--mat-sys-surface-container-lowest);\n}\n\n\n\n.loading-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n gap: 1rem;\n color: var(--mat-sys-on-surface-variant);\n}\n\n\n\n\n.config-content[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n padding: 1.5rem;\n overflow: hidden;\n}\n\n.config-description[_ngcontent-%COMP%] {\n margin: 0 0 1.5rem 0;\n color: var(--mat-sys-on-surface-variant);\n font: var(--mat-sys-body-large);\n line-height: 1.5;\n}\n\n\n\n.panels-container[_ngcontent-%COMP%] {\n flex: 1;\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 1.5rem;\n overflow: hidden;\n}\n\n@media (max-width: 900px) {\n .panels-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n overflow-y: auto;\n }\n\n \n\n .panel-header[_ngcontent-%COMP%] .collapse-toggle[_ngcontent-%COMP%] {\n display: inline-block !important;\n color: var(--mat-sys-on-surface-variant) !important;\n }\n\n \n\n .panel-header[_ngcontent-%COMP%] {\n cursor: pointer;\n user-select: none;\n }\n\n .panel-header[_ngcontent-%COMP%]:hover {\n background: var(--mat-sys-surface-container);\n }\n\n \n\n .panel.collapsed[_ngcontent-%COMP%] {\n flex: 0 0 auto !important;\n }\n\n .panel.collapsed[_ngcontent-%COMP%] .panel-content[_ngcontent-%COMP%] {\n display: none !important;\n }\n\n .panel.collapsed[_ngcontent-%COMP%] .panel-header[_ngcontent-%COMP%] {\n border-bottom: none;\n }\n\n \n\n .panel[_ngcontent-%COMP%]:not(.collapsed) {\n flex: 1 1 auto;\n min-height: 150px;\n }\n}\n\n\n\n.panel[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n background: var(--mat-sys-surface);\n border: 1px solid var(--mat-sys-outline-variant);\n border-radius: var(--mat-sys-corner-medium);\n overflow: hidden;\n}\n\n.panel-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n padding: 1rem 1.25rem;\n background: var(--mat-sys-surface-container);\n border-bottom: 1px solid var(--mat-sys-outline-variant);\n}\n\n.panel-header[_ngcontent-%COMP%] i[_ngcontent-%COMP%]:first-child {\n font-size: 1.1rem;\n color: var(--mat-sys-primary);\n}\n\n.panel-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font: var(--mat-sys-title-medium);\n color: var(--mat-sys-on-surface);\n flex: 1;\n}\n\n.panel-header[_ngcontent-%COMP%] .badge[_ngcontent-%COMP%] {\n background: var(--mat-sys-primary);\n color: var(--mat-sys-on-primary);\n padding: 0.125rem 0.625rem;\n border-radius: var(--mat-sys-corner-full);\n font: var(--mat-sys-label-medium);\n font-weight: 500;\n}\n\n\n\n.panel-header[_ngcontent-%COMP%] .collapse-toggle[_ngcontent-%COMP%] {\n display: none;\n color: var(--mat-sys-on-surface-variant);\n font-size: 0.9rem;\n margin-left: 0.5rem;\n}\n\n.panel-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 0.75rem;\n}\n\n\n\n\n.sortable-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n\n\n.app-item[_ngcontent-%COMP%] {\n cursor: grab;\n transition: transform 0.2s, opacity 0.2s;\n}\n\n.app-item[_ngcontent-%COMP%]:active {\n cursor: grabbing;\n}\n\n.app-item.dragging[_ngcontent-%COMP%] {\n opacity: 0.5;\n transform: scale(0.98);\n}\n\n\n\n\n\n\n.app-item-content[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n padding: var(--mj-space-3) var(--mj-space-3-5);\n \n\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n transition: border-color var(--mj-transition-fast), background var(--mj-transition-fast);\n position: relative; \n\n}\n\n.app-item-content[_ngcontent-%COMP%]:hover, \n.app-item-content[_ngcontent-%COMP%]:focus-within {\n border-color: color-mix(in srgb, var(--mj-brand-primary) 45%, var(--mj-border-default));\n background: var(--mj-bg-surface-hover);\n}\n\n.drag-handle[_ngcontent-%COMP%] {\n color: var(--mat-sys-on-surface-variant);\n cursor: grab;\n padding: 0.25rem;\n}\n\n.drag-handle[_ngcontent-%COMP%]:active {\n cursor: grabbing;\n}\n\n\n\n\n.app-icon[_ngcontent-%COMP%] {\n width: 40px;\n height: 40px;\n border-radius: var(--mj-radius-md);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--app-color, var(--mj-text-muted));\n background: color-mix(in srgb, var(--app-color, var(--mj-text-muted)) 14%, var(--mj-bg-surface-card));\n font-size: var(--mj-text-lg);\n flex-shrink: 0;\n}\n\n.app-info[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 0.125rem;\n min-width: 0;\n}\n\n.app-name[_ngcontent-%COMP%] {\n font-size: var(--mj-text-sm);\n font-weight: var(--mj-font-bold);\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.app-description[_ngcontent-%COMP%] {\n font-size: var(--mj-text-xs);\n line-height: var(--mj-leading-snug);\n color: var(--mj-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n\n\n\n\n\n.app-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.25rem;\n flex-shrink: 0;\n position: absolute;\n right: var(--mj-space-2);\n top: 50%;\n transform: translateY(-50%);\n padding-left: var(--mj-space-6);\n \n\n\n background: linear-gradient(to right, transparent, var(--mj-bg-surface-hover) 1.25rem);\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--mj-transition-fast);\n}\n\n.app-item-content[_ngcontent-%COMP%]:hover .app-actions[_ngcontent-%COMP%], \n.app-item-content[_ngcontent-%COMP%]:focus-within .app-actions[_ngcontent-%COMP%] {\n opacity: 1;\n pointer-events: auto;\n}\n\n\n\n\n\n\n@media (hover: none) {\n .app-actions[_ngcontent-%COMP%] {\n position: static;\n transform: none;\n padding-left: 0;\n background: none;\n opacity: 1;\n pointer-events: auto;\n }\n}\n\n\n\n\n\n\n\n.app-actions[_ngcontent-%COMP%] > button[_ngcontent-%COMP%] {\n width: 1.75rem;\n height: 1.75rem;\n min-width: 0;\n padding: 0;\n font-size: 0.8rem;\n justify-content: center;\n}\n\n\n\n\n.app-actions[_ngcontent-%COMP%] > .remove-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error);\n}\n\n\n\n.available-app-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n padding: var(--mj-space-3) var(--mj-space-3-5);\n margin-bottom: var(--mj-space-2);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n transition: border-color var(--mj-transition-fast), background var(--mj-transition-fast);\n}\n\n.available-app-item[_ngcontent-%COMP%]:hover {\n border-color: color-mix(in srgb, var(--mj-brand-primary) 45%, var(--mj-border-default));\n background: var(--mj-bg-surface-hover);\n}\n\n.available-app-item[_ngcontent-%COMP%] .app-info[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n\n\n.dialog-footer[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1rem 1.5rem;\n background: var(--mat-sys-surface);\n border-top: 1px solid var(--mat-sys-outline-variant);\n flex-shrink: 0;\n}\n\n.footer-left[_ngcontent-%COMP%], \n.footer-right[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.75rem;\n}\n\n.dialog-footer[_ngcontent-%COMP%] button[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n\n\n\n\n\n\n\n\n.mobile-view[_ngcontent-%COMP%] {\n display: none;\n}\n\n@media (max-width: 768px) {\n \n\n .desktop-only[_ngcontent-%COMP%] {\n display: none !important;\n }\n\n .config-description[_ngcontent-%COMP%] {\n display: none;\n }\n\n .mobile-view[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n flex: 1;\n overflow: hidden;\n }\n\n .mobile-list[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 0.5rem;\n }\n\n \n\n .mobile-section-label[_ngcontent-%COMP%] {\n padding: 0.5rem 0.625rem 0.25rem;\n font-size: 0.6875rem;\n font-weight: 600;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n color: var(--mj-text-secondary);\n }\n\n \n\n .mobile-app-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n padding: 0.625rem;\n border-radius: var(--mj-radius-md);\n transition: background-color 0.15s ease;\n }\n\n .mobile-app-row.inactive[_ngcontent-%COMP%] {\n opacity: 0.7;\n }\n\n .mobile-app-row[_ngcontent-%COMP%] .app-icon[_ngcontent-%COMP%] {\n width: 2rem;\n height: 2rem;\n font-size: 0.875rem;\n flex-shrink: 0;\n }\n\n .mobile-app-name[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n \n\n .mobile-drag-handle[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 1.5rem;\n flex-shrink: 0;\n color: var(--mj-text-muted);\n font-size: 0.875rem;\n touch-action: none;\n cursor: grab;\n padding: 0.5rem 0;\n }\n\n .mobile-drag-handle[_ngcontent-%COMP%]:active {\n cursor: grabbing;\n color: var(--mj-text-primary);\n }\n\n \n\n .mobile-app-row.touch-dragging[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-hover);\n box-shadow: var(--mj-shadow-lg);\n border-radius: var(--mj-radius-md);\n position: relative;\n z-index: 100;\n opacity: 0.95;\n }\n\n \n\n .mobile-app-row.touch-ghost[_ngcontent-%COMP%] {\n opacity: 0.3;\n }\n\n \n\n .mobile-drop-indicator[_ngcontent-%COMP%] {\n height: 2px;\n background: var(--mj-brand-primary);\n border-radius: 1px;\n margin: 0 0.625rem;\n }\n\n \n\n .mobile-toggle[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n background: transparent;\n padding: 0.25rem;\n cursor: pointer;\n font-size: 1.5rem;\n flex-shrink: 0;\n transition: color 0.15s ease;\n min-width: 44px;\n min-height: 44px;\n }\n\n .mobile-toggle.on[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n }\n\n .mobile-toggle.off[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n }\n\n \n\n .config-content[_ngcontent-%COMP%] {\n padding: 0;\n }\n\n \n\n .dialog-footer[_ngcontent-%COMP%] {\n padding: 0.75rem;\n gap: 0.5rem;\n }\n\n .footer-left[_ngcontent-%COMP%], \n .footer-right[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.5rem;\n }\n\n}\n\n\n\n@media (prefers-reduced-motion: reduce) {\n .app-item[_ngcontent-%COMP%], \n .app-item-content[_ngcontent-%COMP%], \n .available-app-item[_ngcontent-%COMP%], \n .app-actions[_ngcontent-%COMP%] {\n transition: none;\n }\n}"] });
743
+ }
744
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(UserAppConfigContentComponent, [{
745
+ type: Component,
746
+ args: [{ standalone: false, selector: 'mj-user-app-config-content', template: "<div class=\"app-config-container\">\n <!-- Loading State -->\n @if (IsLoading) {\n <div class=\"loading-container\" role=\"status\" aria-live=\"polite\">\n <mj-loading text=\"Loading your app configuration...\" size=\"medium\"></mj-loading>\n </div>\n }\n\n <!-- Error Message -->\n @if (ErrorMessage) {\n <mj-alert Variant=\"error\">{{ ErrorMessage }}</mj-alert>\n }\n\n <!-- Main Content -->\n @if (!IsLoading) {\n <div class=\"config-content\">\n <p class=\"config-description\">\n Select which applications appear in your app switcher and arrange them in your preferred order.\n Double-click to quickly add or remove. Drag and drop to reorder, or use the arrow buttons.\n </p>\n\n <!-- Desktop: two-panel layout with drag-and-drop -->\n <div class=\"panels-container desktop-only\">\n <!-- Available Apps Panel -->\n <div class=\"panel available-apps-panel\" [class.collapsed]=\"AvailablePanelCollapsed\">\n <div class=\"panel-header\" (click)=\"AvailablePanelCollapsed = !AvailablePanelCollapsed\">\n <i class=\"fa-solid fa-layer-group\" aria-hidden=\"true\"></i>\n <h3>Available Applications</h3>\n <span class=\"badge\">{{ AvailableApps.length }}</span>\n <i\n class=\"collapse-toggle fa-solid\"\n [class.fa-chevron-down]=\"AvailablePanelCollapsed\"\n [class.fa-chevron-up]=\"!AvailablePanelCollapsed\"\n aria-hidden=\"true\"\n ></i>\n </div>\n\n <div class=\"panel-content\">\n @if (AvailableApps.length === 0) {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-check-double\"\n Title=\"All applications added!\" />\n }\n\n @for (item of AvailableApps; track item.app.ID) {\n <div class=\"available-app-item\" (dblclick)=\"AddApp(item)\">\n <div class=\"app-icon\" [style.--app-color]=\"item.app.GetColor()\">\n <i [class]=\"item.app.Icon || 'fa-solid fa-cube'\" aria-hidden=\"true\"></i>\n </div>\n\n <div class=\"app-info\">\n <span class=\"app-name\">{{ item.app.Name }}</span>\n @if (item.app.Description) {\n <span class=\"app-description\">{{ item.app.Description }}</span>\n }\n </div>\n\n <button\n mjButton variant=\"outline\" size=\"sm\"\n (click)=\"AddApp(item)\"\n title=\"Add to your apps\"\n [attr.aria-label]=\"'Add ' + item.app.Name\"\n >\n <i class=\"fa-solid fa-plus\" aria-hidden=\"true\"></i>\n Add\n </button>\n </div>\n }\n </div>\n </div>\n\n <!-- Selected Apps Panel -->\n <div class=\"panel selected-apps-panel\" [class.collapsed]=\"SelectedPanelCollapsed\">\n <div class=\"panel-header\" (click)=\"SelectedPanelCollapsed = !SelectedPanelCollapsed\">\n <i class=\"fa-solid fa-check-circle\" aria-hidden=\"true\"></i>\n <h3>Your Applications</h3>\n <span class=\"badge\">{{ ActiveApps.length }}</span>\n <i\n class=\"collapse-toggle fa-solid\"\n [class.fa-chevron-down]=\"SelectedPanelCollapsed\"\n [class.fa-chevron-up]=\"!SelectedPanelCollapsed\"\n aria-hidden=\"true\"\n ></i>\n </div>\n\n <div class=\"panel-content\">\n @if (ActiveApps.length === 0) {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-inbox\"\n Title=\"No applications selected\"\n Message=\"Add apps from the available list\" />\n }\n\n @if (ActiveApps.length > 0) {\n <div\n class=\"sortable-list\"\n (dragover)=\"OnDragOver($event)\"\n (drop)=\"OnDrop($event)\"\n >\n @for (item of ActiveApps; track item.app.ID; let i = $index) {\n <div\n class=\"app-item\"\n [class.dragging]=\"DraggedItem === item\"\n draggable=\"true\"\n (dragstart)=\"OnDragStart($event, item, i)\"\n (dragend)=\"OnDragEnd()\"\n (dragenter)=\"OnDragEnter($event, i)\"\n (dblclick)=\"RemoveApp(item); $event.stopPropagation()\"\n >\n <div class=\"app-item-content\">\n <div class=\"drag-handle\">\n <i class=\"fa-solid fa-grip-vertical\" aria-hidden=\"true\"></i>\n </div>\n\n <div class=\"app-icon\" [style.--app-color]=\"item.app.GetColor()\">\n <i [class]=\"item.app.Icon || 'fa-solid fa-cube'\" aria-hidden=\"true\"></i>\n </div>\n\n <div class=\"app-info\">\n <span class=\"app-name\">{{ item.app.Name }}</span>\n @if (item.app.Description) {\n <span class=\"app-description\">{{ item.app.Description }}</span>\n }\n </div>\n\n <div class=\"app-actions\">\n <button\n mjButton variant=\"flat\" size=\"sm\"\n [disabled]=\"i === 0\"\n (click)=\"MoveUp(item); $event.stopPropagation()\"\n title=\"Move up\"\n [attr.aria-label]=\"'Move ' + item.app.Name + ' up'\"\n >\n <i class=\"fa-solid fa-chevron-up\" aria-hidden=\"true\"></i>\n </button>\n <button\n mjButton variant=\"flat\" size=\"sm\"\n [disabled]=\"i === ActiveApps.length - 1\"\n (click)=\"MoveDown(item); $event.stopPropagation()\"\n title=\"Move down\"\n [attr.aria-label]=\"'Move ' + item.app.Name + ' down'\"\n >\n <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </button>\n <button\n mjButton variant=\"flat\" size=\"sm\"\n class=\"remove-btn\"\n (click)=\"RemoveApp(item); $event.stopPropagation()\"\n title=\"Remove from your apps\"\n [attr.aria-label]=\"'Remove ' + item.app.Name\"\n >\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n </div>\n\n <!-- Mobile: single list with toggles + touch drag reorder -->\n <div\n class=\"mobile-view\"\n (touchmove)=\"OnTouchDragMove($event)\"\n (touchend)=\"OnTouchDragEnd()\"\n (touchcancel)=\"OnTouchDragEnd()\"\n >\n <div class=\"mobile-list\">\n @if (ActiveApps.length > 0) {\n <div class=\"mobile-section-label\">Your Apps</div>\n @for (item of ActiveApps; track item.app.ID; let i = $index) {\n @if (TouchDragActive && TouchDropIndex === i && TouchDragIndex > i) {\n <div class=\"mobile-drop-indicator\"></div>\n }\n <div\n class=\"mobile-app-row\"\n [class.touch-ghost]=\"TouchDragActive && TouchDragIndex === i\"\n >\n <div\n class=\"mobile-drag-handle\"\n (touchstart)=\"OnTouchDragStart($event, i)\"\n aria-label=\"Drag to reorder\"\n >\n <i class=\"fa-solid fa-grip-vertical\" aria-hidden=\"true\"></i>\n </div>\n <div class=\"app-icon\" [style.--app-color]=\"item.app.GetColor()\">\n <i [class]=\"item.app.Icon || 'fa-solid fa-cube'\" aria-hidden=\"true\"></i>\n </div>\n <span class=\"mobile-app-name\">{{ item.app.Name }}</span>\n <button\n class=\"mobile-toggle on\"\n (click)=\"ToggleApp(item)\"\n aria-label=\"Remove {{ item.app.Name }}\"\n >\n <i class=\"fa-solid fa-toggle-on\" aria-hidden=\"true\"></i>\n </button>\n </div>\n @if (TouchDragActive && TouchDropIndex === i && TouchDragIndex < i) {\n <div class=\"mobile-drop-indicator\"></div>\n }\n }\n @if (TouchDragActive && TouchDropIndex === ActiveApps.length - 1 && TouchDragIndex < TouchDropIndex) {\n <div class=\"mobile-drop-indicator\"></div>\n }\n }\n\n @if (AvailableApps.length > 0) {\n <div class=\"mobile-section-label\">Available</div>\n @for (item of AvailableApps; track item.app.ID) {\n <div class=\"mobile-app-row inactive\">\n <div class=\"app-icon\" [style.--app-color]=\"item.app.GetColor()\">\n <i [class]=\"item.app.Icon || 'fa-solid fa-cube'\" aria-hidden=\"true\"></i>\n </div>\n <span class=\"mobile-app-name\">{{ item.app.Name }}</span>\n <button\n class=\"mobile-toggle off\"\n (click)=\"ToggleApp(item)\"\n aria-label=\"Add {{ item.app.Name }}\"\n >\n <i class=\"fa-solid fa-toggle-off\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Footer Actions -->\n <div class=\"dialog-footer\">\n <div class=\"footer-left\">\n @if (HasChanges()) {\n <button\n mjButton\n (click)=\"Reset()\"\n [disabled]=\"IsLoading || IsSaving\"\n >\n <i class=\"fa-solid fa-rotate-left\" aria-hidden=\"true\"></i>\n Reset Changes\n </button>\n }\n </div>\n <div class=\"footer-right\">\n <button\n mjButton variant=\"primary\"\n (click)=\"Save()\"\n [disabled]=\"IsLoading || IsSaving || !HasChanges()\"\n >\n @if (IsSaving) {\n <mj-loading [showText]=\"false\" size=\"small\"></mj-loading>\n <span>Saving...</span>\n } @else {\n <i class=\"fa-solid fa-check\" aria-hidden=\"true\"></i>\n <span>Save Changes</span>\n }\n </button>\n <button\n mjButton\n (click)=\"Cancel()\"\n [disabled]=\"IsSaving\"\n >\n Cancel\n </button>\n </div>\n </div>\n</div>\n", styles: ["/* ============================================\n User App Config Content (chrome-free)\n Hosted by the full-screen dialog wrapper AND\n the shell app launcher's in-panel config view.\n ============================================ */\n\n:host {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-height: 0;\n}\n\n/* Main container */\n.app-config-container {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n background: var(--mat-sys-surface-container-lowest);\n}\n\n/* Loading state */\n.loading-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n gap: 1rem;\n color: var(--mat-sys-on-surface-variant);\n}\n\n\n/* Config content */\n.config-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n padding: 1.5rem;\n overflow: hidden;\n}\n\n.config-description {\n margin: 0 0 1.5rem 0;\n color: var(--mat-sys-on-surface-variant);\n font: var(--mat-sys-body-large);\n line-height: 1.5;\n}\n\n/* Panels container */\n.panels-container {\n flex: 1;\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 1.5rem;\n overflow: hidden;\n}\n\n@media (max-width: 900px) {\n .panels-container {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n overflow-y: auto;\n }\n\n /* Show collapse toggle on mobile */\n .panel-header .collapse-toggle {\n display: inline-block !important;\n color: var(--mat-sys-on-surface-variant) !important;\n }\n\n /* Make header clickable on mobile */\n .panel-header {\n cursor: pointer;\n user-select: none;\n }\n\n .panel-header:hover {\n background: var(--mat-sys-surface-container);\n }\n\n /* Collapsed panel - just show header */\n .panel.collapsed {\n flex: 0 0 auto !important;\n }\n\n .panel.collapsed .panel-content {\n display: none !important;\n }\n\n .panel.collapsed .panel-header {\n border-bottom: none;\n }\n\n /* Non-collapsed panel takes available space */\n .panel:not(.collapsed) {\n flex: 1 1 auto;\n min-height: 150px;\n }\n}\n\n/* Panel styles */\n.panel {\n display: flex;\n flex-direction: column;\n background: var(--mat-sys-surface);\n border: 1px solid var(--mat-sys-outline-variant);\n border-radius: var(--mat-sys-corner-medium);\n overflow: hidden;\n}\n\n.panel-header {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n padding: 1rem 1.25rem;\n background: var(--mat-sys-surface-container);\n border-bottom: 1px solid var(--mat-sys-outline-variant);\n}\n\n.panel-header i:first-child {\n font-size: 1.1rem;\n color: var(--mat-sys-primary);\n}\n\n.panel-header h3 {\n margin: 0;\n font: var(--mat-sys-title-medium);\n color: var(--mat-sys-on-surface);\n flex: 1;\n}\n\n.panel-header .badge {\n background: var(--mat-sys-primary);\n color: var(--mat-sys-on-primary);\n padding: 0.125rem 0.625rem;\n border-radius: var(--mat-sys-corner-full);\n font: var(--mat-sys-label-medium);\n font-weight: 500;\n}\n\n/* Collapse toggle - hidden by default, shown on mobile */\n.panel-header .collapse-toggle {\n display: none;\n color: var(--mat-sys-on-surface-variant);\n font-size: 0.9rem;\n margin-left: 0.5rem;\n}\n\n.panel-content {\n flex: 1;\n overflow-y: auto;\n padding: 0.75rem;\n}\n\n\n/* Sortable list container */\n.sortable-list {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n/* App items */\n.app-item {\n cursor: grab;\n transition: transform 0.2s, opacity 0.2s;\n}\n\n.app-item:active {\n cursor: grabbing;\n}\n\n.app-item.dragging {\n opacity: 0.5;\n transform: scale(0.98);\n}\n\n/* Rows share the launcher's app-card language: surface bg, default border,\n radius-lg, brand-tinted border + surface-hover on hover. Identity lives in\n the tinted glyph (no left color bar). No hover lift here \u2014 these rows are\n drag-reorder targets and a transform would fight the drag affordance. */\n.app-item-content {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n padding: var(--mj-space-3) var(--mj-space-3-5);\n /* surface-card so the rows keep card depth on white panels in light mode */\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n transition: border-color var(--mj-transition-fast), background var(--mj-transition-fast);\n position: relative; /* Anchor for the hover-reveal action cluster */\n}\n\n.app-item-content:hover,\n.app-item-content:focus-within {\n border-color: color-mix(in srgb, var(--mj-brand-primary) 45%, var(--mj-border-default));\n background: var(--mj-bg-surface-hover);\n}\n\n.drag-handle {\n color: var(--mat-sys-on-surface-variant);\n cursor: grab;\n padding: 0.25rem;\n}\n\n.drag-handle:active {\n cursor: grabbing;\n}\n\n/* Identity glyph \u2014 the launcher card treatment: color-tinted tile with the\n app-colored icon, driven by --app-color from the template. */\n.app-icon {\n width: 40px;\n height: 40px;\n border-radius: var(--mj-radius-md);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--app-color, var(--mj-text-muted));\n background: color-mix(in srgb, var(--app-color, var(--mj-text-muted)) 14%, var(--mj-bg-surface-card));\n font-size: var(--mj-text-lg);\n flex-shrink: 0;\n}\n\n.app-info {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 0.125rem;\n min-width: 0;\n}\n\n.app-name {\n font-size: var(--mj-text-sm);\n font-weight: var(--mj-font-bold);\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.app-description {\n font-size: var(--mj-text-xs);\n line-height: var(--mj-leading-snug);\n color: var(--mj-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Hover-reveal actions: out of the flex flow (absolute over the row's right\n edge) so the title/description get the full row width; revealed on row\n hover or keyboard focus within the row. The left gradient fades the\n underlying text out beneath the cluster for legibility. */\n.app-actions {\n display: flex;\n gap: 0.25rem;\n flex-shrink: 0;\n position: absolute;\n right: var(--mj-space-2);\n top: 50%;\n transform: translateY(-50%);\n padding-left: var(--mj-space-6);\n /* Fades to the row's HOVER background \u2014 the cluster is only ever visible\n while the row is hovered/focused, when that bg is active. */\n background: linear-gradient(to right, transparent, var(--mj-bg-surface-hover) 1.25rem);\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--mj-transition-fast);\n}\n\n.app-item-content:hover .app-actions,\n.app-item-content:focus-within .app-actions {\n opacity: 1;\n pointer-events: auto;\n}\n\n/* Touch devices have no hover: keep the actions permanently visible and in\n the normal flex flow (the hover-reveal overlay would be unreachable, and\n HTML5 drag reorder doesn't work on touch \u2014 buttons are the only reorder\n path there). */\n@media (hover: none) {\n .app-actions {\n position: static;\n transform: none;\n padding-left: 0;\n background: none;\n opacity: 1;\n pointer-events: auto;\n }\n}\n\n\n/* Compact icon-square sizing for the row actions. Wrapper-scoped element\n selector per the mjButton rule (never override .mj-btn classes; the old\n single-dash .mj-btn-ghost/-sm/-danger selectors here matched nothing the\n directive emits and were dead). */\n.app-actions > button {\n width: 1.75rem;\n height: 1.75rem;\n min-width: 0;\n padding: 0;\n font-size: 0.8rem;\n justify-content: center;\n}\n\n/* Remove is a quiet flat button that turns danger-tinted on hover \u2014 a solid\n red button appearing on every row hover is too loud for a tertiary action */\n.app-actions > .remove-btn:hover:not(:disabled) {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error);\n}\n\n/* Available app items \u2014 same card language as the selected rows */\n.available-app-item {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n padding: var(--mj-space-3) var(--mj-space-3-5);\n margin-bottom: var(--mj-space-2);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n transition: border-color var(--mj-transition-fast), background var(--mj-transition-fast);\n}\n\n.available-app-item:hover {\n border-color: color-mix(in srgb, var(--mj-brand-primary) 45%, var(--mj-border-default));\n background: var(--mj-bg-surface-hover);\n}\n\n.available-app-item .app-info {\n flex: 1;\n}\n\n/* Footer actions */\n.dialog-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 1rem 1.5rem;\n background: var(--mat-sys-surface);\n border-top: 1px solid var(--mat-sys-outline-variant);\n flex-shrink: 0;\n}\n\n.footer-left,\n.footer-right {\n display: flex;\n gap: 0.75rem;\n}\n\n.dialog-footer button {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n\n/* ==============================================\n Mobile: single list with toggles\n ============================================== */\n\n/* Hidden by default on desktop */\n.mobile-view {\n display: none;\n}\n\n@media (max-width: 768px) {\n /* Swap layouts */\n .desktop-only {\n display: none !important;\n }\n\n .config-description {\n display: none;\n }\n\n .mobile-view {\n display: flex;\n flex-direction: column;\n flex: 1;\n overflow: hidden;\n }\n\n .mobile-list {\n flex: 1;\n overflow-y: auto;\n padding: 0.5rem;\n }\n\n /* Section labels */\n .mobile-section-label {\n padding: 0.5rem 0.625rem 0.25rem;\n font-size: 0.6875rem;\n font-weight: 600;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n color: var(--mj-text-secondary);\n }\n\n /* App rows */\n .mobile-app-row {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n padding: 0.625rem;\n border-radius: var(--mj-radius-md);\n transition: background-color 0.15s ease;\n }\n\n .mobile-app-row.inactive {\n opacity: 0.7;\n }\n\n .mobile-app-row .app-icon {\n width: 2rem;\n height: 2rem;\n font-size: 0.875rem;\n flex-shrink: 0;\n }\n\n .mobile-app-name {\n flex: 1;\n min-width: 0;\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n /* Drag handle */\n .mobile-drag-handle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 1.5rem;\n flex-shrink: 0;\n color: var(--mj-text-muted);\n font-size: 0.875rem;\n touch-action: none;\n cursor: grab;\n padding: 0.5rem 0;\n }\n\n .mobile-drag-handle:active {\n cursor: grabbing;\n color: var(--mj-text-primary);\n }\n\n /* Dragging row */\n .mobile-app-row.touch-dragging {\n background: var(--mj-bg-surface-hover);\n box-shadow: var(--mj-shadow-lg);\n border-radius: var(--mj-radius-md);\n position: relative;\n z-index: 100;\n opacity: 0.95;\n }\n\n /* Ghost placeholder for the original position */\n .mobile-app-row.touch-ghost {\n opacity: 0.3;\n }\n\n /* Drop indicator line */\n .mobile-drop-indicator {\n height: 2px;\n background: var(--mj-brand-primary);\n border-radius: 1px;\n margin: 0 0.625rem;\n }\n\n /* Toggle button */\n .mobile-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n background: transparent;\n padding: 0.25rem;\n cursor: pointer;\n font-size: 1.5rem;\n flex-shrink: 0;\n transition: color 0.15s ease;\n min-width: 44px;\n min-height: 44px;\n }\n\n .mobile-toggle.on {\n color: var(--mj-brand-primary);\n }\n\n .mobile-toggle.off {\n color: var(--mj-text-muted);\n }\n\n /* Content compact */\n .config-content {\n padding: 0;\n }\n\n /* Footer compact */\n .dialog-footer {\n padding: 0.75rem;\n gap: 0.5rem;\n }\n\n .footer-left,\n .footer-right {\n display: flex;\n gap: 0.5rem;\n }\n\n}\n\n/* Accessibility - Reduced Motion */\n@media (prefers-reduced-motion: reduce) {\n .app-item,\n .app-item-content,\n .available-app-item,\n .app-actions {\n transition: none;\n }\n}\n"] }]
747
+ }], null, { Saved: [{
748
+ type: Output
749
+ }], Cancelled: [{
750
+ type: Output
751
+ }] }); })();
752
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(UserAppConfigContentComponent, { className: "UserAppConfigContentComponent", filePath: "src/lib/user-app-config/user-app-config-content.component.ts", lineNumber: 40 }); })();
753
+ //# sourceMappingURL=user-app-config-content.component.js.map