@memberjunction/ng-explorer-settings 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.
|
@@ -68,6 +68,8 @@ function UserAppConfigComponent_Conditional_0_Conditional_11_For_15_Template(rf,
|
|
|
68
68
|
i0.ɵɵtextInterpolate(item_r5.app.Name);
|
|
69
69
|
i0.ɵɵadvance();
|
|
70
70
|
i0.ɵɵconditional(item_r5.app.Description ? 6 : -1);
|
|
71
|
+
i0.ɵɵadvance();
|
|
72
|
+
i0.ɵɵattribute("aria-label", "Add " + item_r5.app.Name);
|
|
71
73
|
} }
|
|
72
74
|
function UserAppConfigComponent_Conditional_0_Conditional_11_Conditional_25_Template(rf, ctx) { if (rf & 1) {
|
|
73
75
|
i0.ɵɵelementStart(0, "div", 27);
|
|
@@ -132,14 +134,18 @@ function UserAppConfigComponent_Conditional_0_Conditional_11_Conditional_26_For_
|
|
|
132
134
|
i0.ɵɵconditional(item_r9.app.Description ? 9 : -1);
|
|
133
135
|
i0.ɵɵadvance(2);
|
|
134
136
|
i0.ɵɵproperty("disabled", ɵ$index_115_r10 === 0);
|
|
137
|
+
i0.ɵɵattribute("aria-label", "Move " + item_r9.app.Name + " up");
|
|
135
138
|
i0.ɵɵadvance(2);
|
|
136
139
|
i0.ɵɵproperty("disabled", ɵ$index_115_r10 === ctx_r1.ActiveApps.length - 1);
|
|
140
|
+
i0.ɵɵattribute("aria-label", "Move " + item_r9.app.Name + " down");
|
|
141
|
+
i0.ɵɵadvance(2);
|
|
142
|
+
i0.ɵɵattribute("aria-label", "Remove " + item_r9.app.Name);
|
|
137
143
|
} }
|
|
138
144
|
function UserAppConfigComponent_Conditional_0_Conditional_11_Conditional_26_Template(rf, ctx) { if (rf & 1) {
|
|
139
145
|
const _r6 = i0.ɵɵgetCurrentView();
|
|
140
146
|
i0.ɵɵelementStart(0, "div", 45);
|
|
141
147
|
i0.ɵɵlistener("dragover", function UserAppConfigComponent_Conditional_0_Conditional_11_Conditional_26_Template_div_dragover_0_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnDragOver($event)); })("drop", function UserAppConfigComponent_Conditional_0_Conditional_11_Conditional_26_Template_div_drop_0_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnDrop($event)); });
|
|
142
|
-
i0.ɵɵrepeaterCreate(1, UserAppConfigComponent_Conditional_0_Conditional_11_Conditional_26_For_2_Template, 17,
|
|
148
|
+
i0.ɵɵrepeaterCreate(1, UserAppConfigComponent_Conditional_0_Conditional_11_Conditional_26_For_2_Template, 17, 15, "div", 46, _forTrack0);
|
|
143
149
|
i0.ɵɵelementEnd();
|
|
144
150
|
} if (rf & 2) {
|
|
145
151
|
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
@@ -255,7 +261,7 @@ function UserAppConfigComponent_Conditional_0_Conditional_11_Template(rf, ctx) {
|
|
|
255
261
|
i0.ɵɵelementEnd();
|
|
256
262
|
i0.ɵɵelementStart(12, "div", 26);
|
|
257
263
|
i0.ɵɵconditionalCreate(13, UserAppConfigComponent_Conditional_0_Conditional_11_Conditional_13_Template, 4, 0, "div", 27);
|
|
258
|
-
i0.ɵɵrepeaterCreate(14, UserAppConfigComponent_Conditional_0_Conditional_11_For_15_Template, 10,
|
|
264
|
+
i0.ɵɵrepeaterCreate(14, UserAppConfigComponent_Conditional_0_Conditional_11_For_15_Template, 10, 7, "div", 28, _forTrack0);
|
|
259
265
|
i0.ɵɵelementEnd()();
|
|
260
266
|
i0.ɵɵelementStart(16, "div", 29)(17, "div", 22);
|
|
261
267
|
i0.ɵɵlistener("click", function UserAppConfigComponent_Conditional_0_Conditional_11_Template_div_click_17_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SelectedPanelCollapsed = !ctx_r1.SelectedPanelCollapsed); });
|
|
@@ -749,7 +755,7 @@ export class UserAppConfigComponent extends BaseAngularComponent {
|
|
|
749
755
|
}
|
|
750
756
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(UserAppConfigComponent, [{
|
|
751
757
|
type: Component,
|
|
752
|
-
args: [{ standalone: false, selector: 'mj-user-app-config', template: "<!-- Modal Backdrop -->\n@if (ShowDialog) {\n <div class=\"modal-backdrop\" (click)=\"Close()\" role=\"presentation\"></div>\n <div\n class=\"modal-dialog full-screen-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"app-config-title\"\n >\n <!-- Dialog Header -->\n <div class=\"modal-header\">\n <h2 class=\"modal-title\" id=\"app-config-title\">\n <i class=\"fa-solid fa-grid-2\" aria-hidden=\"true\"></i>\n Configure Apps\n </h2>\n <button\n class=\"modal-close\"\n (click)=\"Close()\"\n aria-label=\"Close dialog\"\n >\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <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 <div class=\"error-message\" role=\"alert\">\n <i class=\"fa-solid fa-circle-exclamation\" aria-hidden=\"true\"></i>\n <span>{{ ErrorMessage }}</span>\n </div>\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 <div class=\"empty-state\">\n <i class=\"fa-solid fa-check-double\" aria-hidden=\"true\"></i>\n <p>All applications added!</p>\n </div>\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.background-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 >\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 <div class=\"empty-state\">\n <i class=\"fa-solid fa-inbox\" aria-hidden=\"true\"></i>\n <p>No applications selected</p>\n <p class=\"hint\">Add apps from the available list</p>\n </div>\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\" [style.border-left-color]=\"item.app.GetColor()\">\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.background-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 >\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 >\n <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </button>\n <button\n mjButton variant=\"danger\" size=\"sm\"\n (click)=\"RemoveApp(item); $event.stopPropagation()\"\n title=\"Remove from your apps\"\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.background-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.background-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)=\"Close()\"\n [disabled]=\"IsSaving\"\n >\n Cancel\n </button>\n </div>\n </div>\n </div>\n </div>\n}\n", styles: ["/* ============================================\n User App Config - MD3 Design System\n ============================================ */\n\n/* Modal Backdrop */\n.modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100vw;\n height: 100vh;\n background: rgba(0, 0, 0, 0.5);\n z-index: 1000;\n}\n\n/* Full-screen dialog */\n.full-screen-dialog {\n position: fixed;\n top: 0;\n left: 0;\n width: 100vw;\n height: 100vh;\n background: var(--mat-sys-surface);\n z-index: 1001;\n display: flex;\n flex-direction: column;\n}\n\n/* Modal Header */\n.modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 1rem 1.5rem;\n background: var(--mat-sys-surface);\n border-bottom: 1px solid var(--mat-sys-outline-variant);\n}\n\n.modal-title {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n margin: 0;\n font: var(--mat-sys-title-large);\n color: var(--mat-sys-on-surface);\n}\n\n.modal-title i {\n color: var(--mat-sys-primary);\n}\n\n.modal-close {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 2.5rem;\n height: 2.5rem;\n border: none;\n border-radius: var(--mat-sys-corner-full);\n background: transparent;\n color: var(--mat-sys-on-surface-variant);\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.modal-close:hover {\n background: var(--mat-sys-surface-container);\n color: var(--mat-sys-on-surface);\n}\n\n.modal-close:focus-visible {\n outline: 2px solid var(--mat-sys-primary);\n outline-offset: 2px;\n}\n\n/* Main container */\n.app-config-container {\n display: flex;\n flex-direction: column;\n flex: 1;\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/* Error message */\n.error-message {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1.5rem;\n background: var(--mat-sys-error-container);\n color: var(--mat-sys-on-error-container);\n border-bottom: 1px solid var(--mat-sys-error);\n}\n\n.error-message i {\n font-size: 1.1rem;\n}\n\n/* Config content */\n.config-content {\n flex: 1;\n display: flex;\n flex-direction: column;\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/* Empty state */\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 3rem 1.5rem;\n color: var(--mat-sys-on-surface-variant);\n text-align: center;\n}\n\n.empty-state i {\n font-size: 2.5rem;\n margin-bottom: 1rem;\n opacity: 0.5;\n}\n\n.empty-state p {\n margin: 0;\n font: var(--mat-sys-body-large);\n}\n\n.empty-state .hint {\n margin-top: 0.25rem;\n font: var(--mat-sys-body-medium);\n opacity: 0.7;\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.app-item-content {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.75rem 1rem;\n background: var(--mat-sys-surface);\n border: 1px solid var(--mat-sys-outline-variant);\n border-left-width: 4px;\n border-radius: var(--mat-sys-corner-small);\n transition: all 0.2s ease;\n}\n\n.app-item-content:hover {\n border-color: var(--mat-sys-primary);\n box-shadow: var(--mat-sys-elevation-1);\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.app-icon {\n width: 2.5rem;\n height: 2.5rem;\n border-radius: var(--mat-sys-corner-small);\n display: flex;\n align-items: center;\n justify-content: center;\n color: white;\n font-size: 1.1rem;\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: var(--mat-sys-body-large);\n font-weight: 500;\n color: var(--mat-sys-on-surface);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.app-description {\n font: var(--mat-sys-body-small);\n color: var(--mat-sys-on-surface-variant);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.app-actions {\n display: flex;\n gap: 0.25rem;\n flex-shrink: 0;\n}\n\n.app-actions .mj-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: var(--mj-radius-md);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.app-actions .mj-btn-ghost {\n background: transparent;\n color: var(--mj-text-secondary);\n}\n\n.app-actions .mj-btn-ghost:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.app-actions .mj-btn-ghost:disabled {\n opacity: 0.35;\n cursor: not-allowed;\n}\n\n.app-actions .mj-btn-sm {\n width: 1.75rem;\n height: 1.75rem;\n padding: 0;\n font-size: 0.8rem;\n}\n\n.app-actions .mj-btn-danger:hover:not(:disabled) {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error);\n}\n\n/* Available app items */\n.available-app-item {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.75rem 1rem;\n margin-bottom: 0.5rem;\n background: var(--mat-sys-surface);\n border: 1px solid var(--mat-sys-outline-variant);\n border-radius: var(--mat-sys-corner-small);\n transition: all 0.2s ease;\n}\n\n.available-app-item:hover {\n border-color: var(--mat-sys-primary);\n background: var(--mat-sys-surface-container);\n}\n\n.available-app-item .app-info {\n flex: 1;\n}\n\n/* Dialog footer */\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}\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: 600px) {\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 /* Header compact */\n .modal-header {\n padding: 0.75rem 1rem;\n }\n\n .modal-title {\n font-size: 1.125rem;\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 .dialog-footer .mj-btn {\n padding: 0.5rem 0.875rem;\n font-size: 0.8125rem;\n min-height: 38px;\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 .modal-close {\n transition: none;\n }\n}\n"] }]
|
|
758
|
+
args: [{ standalone: false, selector: 'mj-user-app-config', template: "<!-- Modal Backdrop -->\n@if (ShowDialog) {\n <div class=\"modal-backdrop\" (click)=\"Close()\" role=\"presentation\"></div>\n <div\n class=\"modal-dialog full-screen-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"app-config-title\"\n >\n <!-- Dialog Header -->\n <div class=\"modal-header\">\n <h2 class=\"modal-title\" id=\"app-config-title\">\n <i class=\"fa-solid fa-grid-2\" aria-hidden=\"true\"></i>\n Configure Apps\n </h2>\n <button\n class=\"modal-close\"\n (click)=\"Close()\"\n aria-label=\"Close dialog\"\n >\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <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 <div class=\"error-message\" role=\"alert\">\n <i class=\"fa-solid fa-circle-exclamation\" aria-hidden=\"true\"></i>\n <span>{{ ErrorMessage }}</span>\n </div>\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 <div class=\"empty-state\">\n <i class=\"fa-solid fa-check-double\" aria-hidden=\"true\"></i>\n <p>All applications added!</p>\n </div>\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.background-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 <div class=\"empty-state\">\n <i class=\"fa-solid fa-inbox\" aria-hidden=\"true\"></i>\n <p>No applications selected</p>\n <p class=\"hint\">Add apps from the available list</p>\n </div>\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\" [style.border-left-color]=\"item.app.GetColor()\">\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.background-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=\"danger\" size=\"sm\"\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.background-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.background-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)=\"Close()\"\n [disabled]=\"IsSaving\"\n >\n Cancel\n </button>\n </div>\n </div>\n </div>\n </div>\n}\n", styles: ["/* ============================================\n User App Config - MD3 Design System\n ============================================ */\n\n/* Modal Backdrop */\n.modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100vw;\n height: 100vh;\n background: rgba(0, 0, 0, 0.5);\n z-index: 1000;\n}\n\n/* Full-screen dialog */\n.full-screen-dialog {\n position: fixed;\n top: 0;\n left: 0;\n width: 100vw;\n height: 100vh;\n background: var(--mat-sys-surface);\n z-index: 1001;\n display: flex;\n flex-direction: column;\n}\n\n/* Modal Header */\n.modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 1rem 1.5rem;\n background: var(--mat-sys-surface);\n border-bottom: 1px solid var(--mat-sys-outline-variant);\n}\n\n.modal-title {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n margin: 0;\n font: var(--mat-sys-title-large);\n color: var(--mat-sys-on-surface);\n}\n\n.modal-title i {\n color: var(--mat-sys-primary);\n}\n\n.modal-close {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 2.5rem;\n height: 2.5rem;\n border: none;\n border-radius: var(--mat-sys-corner-full);\n background: transparent;\n color: var(--mat-sys-on-surface-variant);\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.modal-close:hover {\n background: var(--mat-sys-surface-container);\n color: var(--mat-sys-on-surface);\n}\n\n.modal-close:focus-visible {\n outline: 2px solid var(--mat-sys-primary);\n outline-offset: 2px;\n}\n\n/* Main container */\n.app-config-container {\n display: flex;\n flex-direction: column;\n flex: 1;\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/* Error message */\n.error-message {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1.5rem;\n background: var(--mat-sys-error-container);\n color: var(--mat-sys-on-error-container);\n border-bottom: 1px solid var(--mat-sys-error);\n}\n\n.error-message i {\n font-size: 1.1rem;\n}\n\n/* Config content */\n.config-content {\n flex: 1;\n display: flex;\n flex-direction: column;\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/* Empty state */\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 3rem 1.5rem;\n color: var(--mat-sys-on-surface-variant);\n text-align: center;\n}\n\n.empty-state i {\n font-size: 2.5rem;\n margin-bottom: 1rem;\n opacity: 0.5;\n}\n\n.empty-state p {\n margin: 0;\n font: var(--mat-sys-body-large);\n}\n\n.empty-state .hint {\n margin-top: 0.25rem;\n font: var(--mat-sys-body-medium);\n opacity: 0.7;\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.app-item-content {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.75rem 1rem;\n background: var(--mat-sys-surface);\n border: 1px solid var(--mat-sys-outline-variant);\n border-left-width: 4px;\n border-radius: var(--mat-sys-corner-small);\n transition: all 0.2s ease;\n}\n\n.app-item-content:hover {\n border-color: var(--mat-sys-primary);\n box-shadow: var(--mat-sys-elevation-1);\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.app-icon {\n width: 2.5rem;\n height: 2.5rem;\n border-radius: var(--mat-sys-corner-small);\n display: flex;\n align-items: center;\n justify-content: center;\n color: white;\n font-size: 1.1rem;\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: var(--mat-sys-body-large);\n font-weight: 500;\n color: var(--mat-sys-on-surface);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.app-description {\n font: var(--mat-sys-body-small);\n color: var(--mat-sys-on-surface-variant);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.app-actions {\n display: flex;\n gap: 0.25rem;\n flex-shrink: 0;\n}\n\n.app-actions .mj-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: var(--mj-radius-md);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.app-actions .mj-btn-ghost {\n background: transparent;\n color: var(--mj-text-secondary);\n}\n\n.app-actions .mj-btn-ghost:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.app-actions .mj-btn-ghost:disabled {\n opacity: 0.35;\n cursor: not-allowed;\n}\n\n.app-actions .mj-btn-sm {\n width: 1.75rem;\n height: 1.75rem;\n padding: 0;\n font-size: 0.8rem;\n}\n\n.app-actions .mj-btn-danger:hover:not(:disabled) {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error);\n}\n\n/* Available app items */\n.available-app-item {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.75rem 1rem;\n margin-bottom: 0.5rem;\n background: var(--mat-sys-surface);\n border: 1px solid var(--mat-sys-outline-variant);\n border-radius: var(--mat-sys-corner-small);\n transition: all 0.2s ease;\n}\n\n.available-app-item:hover {\n border-color: var(--mat-sys-primary);\n background: var(--mat-sys-surface-container);\n}\n\n.available-app-item .app-info {\n flex: 1;\n}\n\n/* Dialog footer */\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}\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: 600px) {\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 /* Header compact */\n .modal-header {\n padding: 0.75rem 1rem;\n }\n\n .modal-title {\n font-size: 1.125rem;\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 .dialog-footer .mj-btn {\n padding: 0.5rem 0.875rem;\n font-size: 0.8125rem;\n min-height: 38px;\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 .modal-close {\n transition: none;\n }\n}\n"] }]
|
|
753
759
|
}], null, { ShowDialog: [{
|
|
754
760
|
type: Input
|
|
755
761
|
}], ShowDialogChange: [{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-app-config.component.js","sourceRoot":"","sources":["../../../src/lib/user-app-config/user-app-config.component.ts","../../../src/lib/user-app-config/user-app-config.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, ChangeDetectorRef, NgZone, ElementRef, inject } from '@angular/core';\nimport { Metadata, LogError, LogStatus, IMetadataProvider, LogStatusEx } from '@memberjunction/core';\nimport { MJUserApplicationEntity, UserInfoEngine } from '@memberjunction/core-entities';\nimport { ApplicationManager, BaseApplication } from '@memberjunction/ng-base-application';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { UUIDsEqual } from '@memberjunction/global';\n\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\n/**\n * Represents an app item in the configuration UI\n */\ninterface AppConfigItem {\n app: BaseApplication;\n userAppId: string | null;\n sequence: number;\n isActive: boolean;\n isDirty: boolean;\n}\n\n/**\n * Full-screen modal dialog for configuring user's application visibility and order.\n * Allows users to:\n * - Select which applications to show in the app switcher\n * - Reorder applications via drag-and-drop (desktop)\n * - Toggle apps on/off in a single list (mobile)\n */\n@Component({\n standalone: false,\n selector: 'mj-user-app-config',\n templateUrl: './user-app-config.component.html',\n styleUrls: ['./user-app-config.component.css']\n})\nexport class UserAppConfigComponent extends BaseAngularComponent {\n private appManager = inject(ApplicationManager);\n private sharedService = inject(SharedService);\n private cdr = inject(ChangeDetectorRef);\n private ngZone = inject(NgZone);\n private el = inject(ElementRef);\n\n private _showDialog = false;\n\n @Input()\n set ShowDialog(value: boolean) {\n if (value !== this._showDialog) {\n this._showDialog = value;\n }\n }\n get ShowDialog(): boolean {\n return this._showDialog;\n }\n\n @Output() ShowDialogChange = new EventEmitter<boolean>();\n @Output() ConfigSaved = new EventEmitter<void>();\n\n AllApps: AppConfigItem[] = [];\n ActiveApps: AppConfigItem[] = [];\n AvailableApps: AppConfigItem[] = [];\n\n IsLoading = false;\n IsSaving = false;\n ErrorMessage = '';\n\n AvailablePanelCollapsed = false;\n SelectedPanelCollapsed = false;\n\n DraggedItem: AppConfigItem | null = null;\n DraggedIndex = -1;\n DropTargetIndex = -1;\n\n /**\n * Opens the dialog and loads user's app configuration\n */\n async Open(): Promise<void> {\n this._showDialog = true;\n this.ShowDialogChange.emit(true);\n this.ErrorMessage = '';\n await this.loadConfiguration();\n }\n\n /**\n * Closes the dialog without saving\n */\n Close(): void {\n this._showDialog = false;\n this.ShowDialogChange.emit(false);\n }\n\n /**\n * Adds an app to the user's active list\n */\n AddApp(item: AppConfigItem): void {\n item.isActive = true;\n item.sequence = this.ActiveApps.length;\n item.isDirty = true;\n this.refreshAppLists();\n }\n\n /**\n * Removes an app from the user's active list\n */\n RemoveApp(item: AppConfigItem): void {\n item.isActive = false;\n item.sequence = 999;\n item.isDirty = true;\n this.refreshAppLists();\n\n this.resequenceActiveApps();\n }\n\n /**\n * Toggles an app between active and inactive\n */\n ToggleApp(item: AppConfigItem): void {\n if (item.isActive) {\n this.RemoveApp(item);\n } else {\n this.AddApp(item);\n }\n }\n\n /**\n * Moves an app up in the order\n */\n MoveUp(item: AppConfigItem): void {\n const index = this.ActiveApps.indexOf(item);\n if (index > 0) {\n this.swapSequences(item, this.ActiveApps[index - 1]);\n this.ActiveApps = [...this.ActiveApps].sort((a, b) => a.sequence - b.sequence);\n }\n }\n\n /**\n * Moves an app down in the order\n */\n MoveDown(item: AppConfigItem): void {\n const index = this.ActiveApps.indexOf(item);\n if (index < this.ActiveApps.length - 1) {\n this.swapSequences(item, this.ActiveApps[index + 1]);\n this.ActiveApps = [...this.ActiveApps].sort((a, b) => a.sequence - b.sequence);\n }\n }\n\n /**\n * Checks if there are any unsaved changes\n */\n HasChanges(): boolean {\n return this.AllApps.some(item => item.isDirty);\n }\n\n /**\n * Saves the user's app configuration\n */\n async Save(): Promise<void> {\n if (!this.HasChanges()) {\n this.Close();\n return;\n }\n\n this.IsSaving = true;\n this.ErrorMessage = '';\n\n try {\n const md = this.ProviderToUse;\n\n for (const item of this.AllApps) {\n if (!item.isDirty) continue;\n\n if (item.userAppId) {\n await this.updateUserApplication(md, item);\n } else if (item.isActive) {\n await this.createUserApplication(md, item);\n }\n }\n\n // Each userApp.Save() above fires a BaseEntity 'save' event, which UserInfoEngine\n // catches via HandleIndividualBaseEntityEvent → debounced refresh → NotifyDataChange.\n // ApplicationManager.subscribeToEngineChanges then propagates to the shell app\n // switcher. No explicit reload needed here — and avoiding it sidesteps NG0100\n // from observable emissions interleaving with this method's save loop.\n LogStatusEx({\n message: 'User app configuration saved',\n verboseOnly: true \n });\n\n this.sharedService.CreateSimpleNotification('App configuration saved successfully!', 'success', 3000);\n this.ConfigSaved.emit();\n this.Close();\n\n } catch (error) {\n this.ErrorMessage = 'Failed to save configuration. Please try again.';\n LogError('Error saving app configuration:', undefined, error instanceof Error ? error.message : String(error));\n } finally {\n this.ngZone.run(() => {\n this.IsSaving = false;\n this.cdr.detectChanges();\n });\n }\n }\n\n /**\n * Resets all changes and reloads the configuration\n */\n async Reset(): Promise<void> {\n await this.loadConfiguration();\n }\n\n // ---------------------------------------------------------------------------\n // Drag-and-drop handlers (desktop only)\n // ---------------------------------------------------------------------------\n\n OnDragStart(event: DragEvent, item: AppConfigItem, index: number): void {\n this.DraggedItem = item;\n this.DraggedIndex = index;\n\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n event.dataTransfer.setData('text/plain', index.toString());\n }\n }\n\n OnDragOver(event: DragEvent): void {\n event.preventDefault();\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = 'move';\n }\n }\n\n OnDragEnter(event: DragEvent, index: number): void {\n event.preventDefault();\n this.DropTargetIndex = index;\n }\n\n OnDragEnd(): void {\n this.DraggedItem = null;\n this.DraggedIndex = -1;\n this.DropTargetIndex = -1;\n }\n\n OnDrop(event: DragEvent): void {\n event.preventDefault();\n\n if (this.DraggedIndex >= 0 && this.DropTargetIndex >= 0 && this.DraggedIndex !== this.DropTargetIndex) {\n const [movedItem] = this.ActiveApps.splice(this.DraggedIndex, 1);\n this.ActiveApps.splice(this.DropTargetIndex, 0, movedItem);\n this.resequenceActiveApps();\n this.cdr.detectChanges();\n }\n\n this.OnDragEnd();\n }\n\n // ---------------------------------------------------------------------------\n // Touch drag-and-drop handlers (mobile)\n // ---------------------------------------------------------------------------\n\n TouchDragIndex = -1;\n TouchDropIndex = -1;\n TouchDragActive = false;\n\n private touchStartY = 0;\n private touchCurrentY = 0;\n private touchRowHeight = 0;\n private touchDragElement: HTMLElement | null = null;\n private touchScrollContainer: HTMLElement | null = null;\n\n OnTouchDragStart(event: TouchEvent, index: number): void {\n const touch = event.touches[0];\n const row = (event.target as HTMLElement).closest('.mobile-app-row') as HTMLElement | null;\n if (!row) return;\n\n event.preventDefault();\n this.TouchDragIndex = index;\n this.TouchDropIndex = index;\n this.TouchDragActive = true;\n this.touchStartY = touch.clientY;\n this.touchCurrentY = touch.clientY;\n this.touchDragElement = row;\n this.touchRowHeight = row.offsetHeight;\n this.touchScrollContainer = this.el.nativeElement.querySelector('.mobile-list');\n\n row.classList.add('touch-dragging');\n }\n\n OnTouchDragMove(event: TouchEvent): void {\n if (!this.TouchDragActive || !this.touchDragElement) return;\n\n event.preventDefault();\n const touch = event.touches[0];\n this.touchCurrentY = touch.clientY;\n const deltaY = this.touchCurrentY - this.touchStartY;\n\n this.touchDragElement.style.transform = `translateY(${deltaY}px)`;\n this.touchDragElement.style.zIndex = '100';\n\n this.updateTouchDropTarget(deltaY);\n this.autoScrollIfNeeded();\n }\n\n OnTouchDragEnd(): void {\n if (!this.TouchDragActive) return;\n\n if (this.touchDragElement) {\n this.touchDragElement.style.transform = '';\n this.touchDragElement.style.zIndex = '';\n this.touchDragElement.classList.remove('touch-dragging');\n }\n\n if (this.TouchDragIndex >= 0 && this.TouchDropIndex >= 0 && this.TouchDragIndex !== this.TouchDropIndex) {\n const [movedItem] = this.ActiveApps.splice(this.TouchDragIndex, 1);\n this.ActiveApps.splice(this.TouchDropIndex, 0, movedItem);\n this.resequenceActiveApps();\n }\n\n this.resetTouchDragState();\n this.cdr.detectChanges();\n }\n\n private updateTouchDropTarget(deltaY: number): void {\n const rowsToMove = Math.round(deltaY / this.touchRowHeight);\n const newIndex = Math.max(0, Math.min(this.ActiveApps.length - 1, this.TouchDragIndex + rowsToMove));\n this.TouchDropIndex = newIndex;\n }\n\n private autoScrollIfNeeded(): void {\n if (!this.touchScrollContainer) return;\n\n const rect = this.touchScrollContainer.getBoundingClientRect();\n const edgeZone = 40;\n\n if (this.touchCurrentY < rect.top + edgeZone) {\n this.touchScrollContainer.scrollTop -= 8;\n } else if (this.touchCurrentY > rect.bottom - edgeZone) {\n this.touchScrollContainer.scrollTop += 8;\n }\n }\n\n private resetTouchDragState(): void {\n this.TouchDragIndex = -1;\n this.TouchDropIndex = -1;\n this.TouchDragActive = false;\n this.touchDragElement = null;\n this.touchScrollContainer = null;\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private async loadConfiguration(): Promise<void> {\n this.IsLoading = true;\n this.ErrorMessage = '';\n\n try {\n // Read directly from UserInfoEngine's cache. The engine already loaded\n // and maintains MJ: User Applications via its event-driven refresh — no\n // need to re-query the DB on every dialog open. EnsureLoaded is idempotent\n // and instant when the engine is already loaded (the typical case here,\n // since UserInfoEngine fires at startup).\n await UserInfoEngine.Instance.EnsureLoaded();\n const systemApps = this.appManager.GetAuthorizedSystemApps();\n const userApps = UserInfoEngine.Instance.UserApplications;\n this.AllApps = this.buildAppConfigItems(systemApps, userApps);\n this.refreshAppLists();\n\n } catch (error) {\n this.ErrorMessage = 'Failed to load app configuration. Please try again.';\n LogError('Error loading app configuration:', undefined, error instanceof Error ? error.message : String(error));\n } finally {\n this.ngZone.run(() => {\n this.IsLoading = false;\n this.cdr.detectChanges();\n });\n }\n }\n\n private buildAppConfigItems(systemApps: BaseApplication[], userApps: MJUserApplicationEntity[]): AppConfigItem[] {\n return systemApps.map(app => {\n const userApp = userApps.find(ua => UUIDsEqual(ua.ApplicationID, app.ID));\n return {\n app,\n userAppId: userApp?.ID ?? null,\n sequence: userApp?.Sequence ?? 999,\n isActive: userApp?.IsActive ?? false,\n isDirty: false\n };\n });\n }\n\n private refreshAppLists(): void {\n this.ActiveApps = this.AllApps\n .filter(item => item.isActive)\n .sort((a, b) => a.sequence - b.sequence);\n\n this.AvailableApps = this.AllApps\n .filter(item => !item.isActive)\n .sort((a, b) => a.app.Name.localeCompare(b.app.Name));\n }\n\n private resequenceActiveApps(): void {\n this.ActiveApps.forEach((item, index) => {\n if (item.sequence !== index) {\n item.sequence = index;\n item.isDirty = true;\n }\n });\n }\n\n private swapSequences(a: AppConfigItem, b: AppConfigItem): void {\n const tempSeq = a.sequence;\n a.sequence = b.sequence;\n b.sequence = tempSeq;\n a.isDirty = true;\n b.isDirty = true;\n }\n\n private async updateUserApplication(md: IMetadataProvider, item: AppConfigItem): Promise<void> {\n const userApp = await md.GetEntityObject<MJUserApplicationEntity>('MJ: User Applications');\n await userApp.Load(item.userAppId!);\n\n userApp.Sequence = item.sequence;\n userApp.IsActive = item.isActive;\n\n const saved = await userApp.Save();\n if (!saved) {\n throw new Error(`Failed to update UserApplication for ${item.app.Name}: ${userApp.LatestResult}`);\n }\n\n item.isDirty = false;\n LogStatus(`Updated UserApplication for ${item.app.Name}: sequence=${item.sequence}, isActive=${item.isActive}`);\n }\n\n private async createUserApplication(md: IMetadataProvider, item: AppConfigItem): Promise<void> {\n const userApp = await md.GetEntityObject<MJUserApplicationEntity>('MJ: User Applications');\n userApp.NewRecord();\n\n userApp.UserID = md.CurrentUser.ID;\n userApp.ApplicationID = item.app.ID;\n userApp.Sequence = item.sequence;\n userApp.IsActive = item.isActive;\n\n const saved = await userApp.Save();\n if (!saved) {\n throw new Error(`Failed to create UserApplication for ${item.app.Name}: ${userApp.LatestResult}`);\n }\n\n item.userAppId = userApp.ID;\n item.isDirty = false;\n LogStatus(`Created UserApplication for ${item.app.Name}: sequence=${item.sequence}`);\n }\n}\n","<!-- Modal Backdrop -->\n@if (ShowDialog) {\n <div class=\"modal-backdrop\" (click)=\"Close()\" role=\"presentation\"></div>\n <div\n class=\"modal-dialog full-screen-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"app-config-title\"\n >\n <!-- Dialog Header -->\n <div class=\"modal-header\">\n <h2 class=\"modal-title\" id=\"app-config-title\">\n <i class=\"fa-solid fa-grid-2\" aria-hidden=\"true\"></i>\n Configure Apps\n </h2>\n <button\n class=\"modal-close\"\n (click)=\"Close()\"\n aria-label=\"Close dialog\"\n >\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <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 <div class=\"error-message\" role=\"alert\">\n <i class=\"fa-solid fa-circle-exclamation\" aria-hidden=\"true\"></i>\n <span>{{ ErrorMessage }}</span>\n </div>\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 <div class=\"empty-state\">\n <i class=\"fa-solid fa-check-double\" aria-hidden=\"true\"></i>\n <p>All applications added!</p>\n </div>\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.background-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 >\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 <div class=\"empty-state\">\n <i class=\"fa-solid fa-inbox\" aria-hidden=\"true\"></i>\n <p>No applications selected</p>\n <p class=\"hint\">Add apps from the available list</p>\n </div>\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\" [style.border-left-color]=\"item.app.GetColor()\">\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.background-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 >\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 >\n <i class=\"fa-solid fa-chevron-down\" aria-hidden=\"true\"></i>\n </button>\n <button\n mjButton variant=\"danger\" size=\"sm\"\n (click)=\"RemoveApp(item); $event.stopPropagation()\"\n title=\"Remove from your apps\"\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.background-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.background-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)=\"Close()\"\n [disabled]=\"IsSaving\"\n >\n Cancel\n </button>\n </div>\n </div>\n </div>\n </div>\n}\n"]}
|
|
1
|
+
{"version":3,"file":"user-app-config.component.js","sourceRoot":"","sources":["../../../src/lib/user-app-config/user-app-config.component.ts","../../../src/lib/user-app-config/user-app-config.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, ChangeDetectorRef, NgZone, ElementRef, inject } from '@angular/core';\nimport { Metadata, LogError, LogStatus, IMetadataProvider, LogStatusEx } from '@memberjunction/core';\nimport { MJUserApplicationEntity, UserInfoEngine } from '@memberjunction/core-entities';\nimport { ApplicationManager, BaseApplication } from '@memberjunction/ng-base-application';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { UUIDsEqual } from '@memberjunction/global';\n\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\n/**\n * Represents an app item in the configuration UI\n */\ninterface AppConfigItem {\n app: BaseApplication;\n userAppId: string | null;\n sequence: number;\n isActive: boolean;\n isDirty: boolean;\n}\n\n/**\n * Full-screen modal dialog for configuring user's application visibility and order.\n * Allows users to:\n * - Select which applications to show in the app switcher\n * - Reorder applications via drag-and-drop (desktop)\n * - Toggle apps on/off in a single list (mobile)\n */\n@Component({\n standalone: false,\n selector: 'mj-user-app-config',\n templateUrl: './user-app-config.component.html',\n styleUrls: ['./user-app-config.component.css']\n})\nexport class UserAppConfigComponent extends BaseAngularComponent {\n private appManager = inject(ApplicationManager);\n private sharedService = inject(SharedService);\n private cdr = inject(ChangeDetectorRef);\n private ngZone = inject(NgZone);\n private el = inject(ElementRef);\n\n private _showDialog = false;\n\n @Input()\n set ShowDialog(value: boolean) {\n if (value !== this._showDialog) {\n this._showDialog = value;\n }\n }\n get ShowDialog(): boolean {\n return this._showDialog;\n }\n\n @Output() ShowDialogChange = new EventEmitter<boolean>();\n @Output() ConfigSaved = new EventEmitter<void>();\n\n AllApps: AppConfigItem[] = [];\n ActiveApps: AppConfigItem[] = [];\n AvailableApps: AppConfigItem[] = [];\n\n IsLoading = false;\n IsSaving = false;\n ErrorMessage = '';\n\n AvailablePanelCollapsed = false;\n SelectedPanelCollapsed = false;\n\n DraggedItem: AppConfigItem | null = null;\n DraggedIndex = -1;\n DropTargetIndex = -1;\n\n /**\n * Opens the dialog and loads user's app configuration\n */\n async Open(): Promise<void> {\n this._showDialog = true;\n this.ShowDialogChange.emit(true);\n this.ErrorMessage = '';\n await this.loadConfiguration();\n }\n\n /**\n * Closes the dialog without saving\n */\n Close(): void {\n this._showDialog = false;\n this.ShowDialogChange.emit(false);\n }\n\n /**\n * Adds an app to the user's active list\n */\n AddApp(item: AppConfigItem): void {\n item.isActive = true;\n item.sequence = this.ActiveApps.length;\n item.isDirty = true;\n this.refreshAppLists();\n }\n\n /**\n * Removes an app from the user's active list\n */\n RemoveApp(item: AppConfigItem): void {\n item.isActive = false;\n item.sequence = 999;\n item.isDirty = true;\n this.refreshAppLists();\n\n this.resequenceActiveApps();\n }\n\n /**\n * Toggles an app between active and inactive\n */\n ToggleApp(item: AppConfigItem): void {\n if (item.isActive) {\n this.RemoveApp(item);\n } else {\n this.AddApp(item);\n }\n }\n\n /**\n * Moves an app up in the order\n */\n MoveUp(item: AppConfigItem): void {\n const index = this.ActiveApps.indexOf(item);\n if (index > 0) {\n this.swapSequences(item, this.ActiveApps[index - 1]);\n this.ActiveApps = [...this.ActiveApps].sort((a, b) => a.sequence - b.sequence);\n }\n }\n\n /**\n * Moves an app down in the order\n */\n MoveDown(item: AppConfigItem): void {\n const index = this.ActiveApps.indexOf(item);\n if (index < this.ActiveApps.length - 1) {\n this.swapSequences(item, this.ActiveApps[index + 1]);\n this.ActiveApps = [...this.ActiveApps].sort((a, b) => a.sequence - b.sequence);\n }\n }\n\n /**\n * Checks if there are any unsaved changes\n */\n HasChanges(): boolean {\n return this.AllApps.some(item => item.isDirty);\n }\n\n /**\n * Saves the user's app configuration\n */\n async Save(): Promise<void> {\n if (!this.HasChanges()) {\n this.Close();\n return;\n }\n\n this.IsSaving = true;\n this.ErrorMessage = '';\n\n try {\n const md = this.ProviderToUse;\n\n for (const item of this.AllApps) {\n if (!item.isDirty) continue;\n\n if (item.userAppId) {\n await this.updateUserApplication(md, item);\n } else if (item.isActive) {\n await this.createUserApplication(md, item);\n }\n }\n\n // Each userApp.Save() above fires a BaseEntity 'save' event, which UserInfoEngine\n // catches via HandleIndividualBaseEntityEvent → debounced refresh → NotifyDataChange.\n // ApplicationManager.subscribeToEngineChanges then propagates to the shell app\n // switcher. No explicit reload needed here — and avoiding it sidesteps NG0100\n // from observable emissions interleaving with this method's save loop.\n LogStatusEx({\n message: 'User app configuration saved',\n verboseOnly: true \n });\n\n this.sharedService.CreateSimpleNotification('App configuration saved successfully!', 'success', 3000);\n this.ConfigSaved.emit();\n this.Close();\n\n } catch (error) {\n this.ErrorMessage = 'Failed to save configuration. Please try again.';\n LogError('Error saving app configuration:', undefined, error instanceof Error ? error.message : String(error));\n } finally {\n this.ngZone.run(() => {\n this.IsSaving = false;\n this.cdr.detectChanges();\n });\n }\n }\n\n /**\n * Resets all changes and reloads the configuration\n */\n async Reset(): Promise<void> {\n await this.loadConfiguration();\n }\n\n // ---------------------------------------------------------------------------\n // Drag-and-drop handlers (desktop only)\n // ---------------------------------------------------------------------------\n\n OnDragStart(event: DragEvent, item: AppConfigItem, index: number): void {\n this.DraggedItem = item;\n this.DraggedIndex = index;\n\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n event.dataTransfer.setData('text/plain', index.toString());\n }\n }\n\n OnDragOver(event: DragEvent): void {\n event.preventDefault();\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = 'move';\n }\n }\n\n OnDragEnter(event: DragEvent, index: number): void {\n event.preventDefault();\n this.DropTargetIndex = index;\n }\n\n OnDragEnd(): void {\n this.DraggedItem = null;\n this.DraggedIndex = -1;\n this.DropTargetIndex = -1;\n }\n\n OnDrop(event: DragEvent): void {\n event.preventDefault();\n\n if (this.DraggedIndex >= 0 && this.DropTargetIndex >= 0 && this.DraggedIndex !== this.DropTargetIndex) {\n const [movedItem] = this.ActiveApps.splice(this.DraggedIndex, 1);\n this.ActiveApps.splice(this.DropTargetIndex, 0, movedItem);\n this.resequenceActiveApps();\n this.cdr.detectChanges();\n }\n\n this.OnDragEnd();\n }\n\n // ---------------------------------------------------------------------------\n // Touch drag-and-drop handlers (mobile)\n // ---------------------------------------------------------------------------\n\n TouchDragIndex = -1;\n TouchDropIndex = -1;\n TouchDragActive = false;\n\n private touchStartY = 0;\n private touchCurrentY = 0;\n private touchRowHeight = 0;\n private touchDragElement: HTMLElement | null = null;\n private touchScrollContainer: HTMLElement | null = null;\n\n OnTouchDragStart(event: TouchEvent, index: number): void {\n const touch = event.touches[0];\n const row = (event.target as HTMLElement).closest('.mobile-app-row') as HTMLElement | null;\n if (!row) return;\n\n event.preventDefault();\n this.TouchDragIndex = index;\n this.TouchDropIndex = index;\n this.TouchDragActive = true;\n this.touchStartY = touch.clientY;\n this.touchCurrentY = touch.clientY;\n this.touchDragElement = row;\n this.touchRowHeight = row.offsetHeight;\n this.touchScrollContainer = this.el.nativeElement.querySelector('.mobile-list');\n\n row.classList.add('touch-dragging');\n }\n\n OnTouchDragMove(event: TouchEvent): void {\n if (!this.TouchDragActive || !this.touchDragElement) return;\n\n event.preventDefault();\n const touch = event.touches[0];\n this.touchCurrentY = touch.clientY;\n const deltaY = this.touchCurrentY - this.touchStartY;\n\n this.touchDragElement.style.transform = `translateY(${deltaY}px)`;\n this.touchDragElement.style.zIndex = '100';\n\n this.updateTouchDropTarget(deltaY);\n this.autoScrollIfNeeded();\n }\n\n OnTouchDragEnd(): void {\n if (!this.TouchDragActive) return;\n\n if (this.touchDragElement) {\n this.touchDragElement.style.transform = '';\n this.touchDragElement.style.zIndex = '';\n this.touchDragElement.classList.remove('touch-dragging');\n }\n\n if (this.TouchDragIndex >= 0 && this.TouchDropIndex >= 0 && this.TouchDragIndex !== this.TouchDropIndex) {\n const [movedItem] = this.ActiveApps.splice(this.TouchDragIndex, 1);\n this.ActiveApps.splice(this.TouchDropIndex, 0, movedItem);\n this.resequenceActiveApps();\n }\n\n this.resetTouchDragState();\n this.cdr.detectChanges();\n }\n\n private updateTouchDropTarget(deltaY: number): void {\n const rowsToMove = Math.round(deltaY / this.touchRowHeight);\n const newIndex = Math.max(0, Math.min(this.ActiveApps.length - 1, this.TouchDragIndex + rowsToMove));\n this.TouchDropIndex = newIndex;\n }\n\n private autoScrollIfNeeded(): void {\n if (!this.touchScrollContainer) return;\n\n const rect = this.touchScrollContainer.getBoundingClientRect();\n const edgeZone = 40;\n\n if (this.touchCurrentY < rect.top + edgeZone) {\n this.touchScrollContainer.scrollTop -= 8;\n } else if (this.touchCurrentY > rect.bottom - edgeZone) {\n this.touchScrollContainer.scrollTop += 8;\n }\n }\n\n private resetTouchDragState(): void {\n this.TouchDragIndex = -1;\n this.TouchDropIndex = -1;\n this.TouchDragActive = false;\n this.touchDragElement = null;\n this.touchScrollContainer = null;\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private async loadConfiguration(): Promise<void> {\n this.IsLoading = true;\n this.ErrorMessage = '';\n\n try {\n // Read directly from UserInfoEngine's cache. The engine already loaded\n // and maintains MJ: User Applications via its event-driven refresh — no\n // need to re-query the DB on every dialog open. EnsureLoaded is idempotent\n // and instant when the engine is already loaded (the typical case here,\n // since UserInfoEngine fires at startup).\n await UserInfoEngine.Instance.EnsureLoaded();\n const systemApps = this.appManager.GetAuthorizedSystemApps();\n const userApps = UserInfoEngine.Instance.UserApplications;\n this.AllApps = this.buildAppConfigItems(systemApps, userApps);\n this.refreshAppLists();\n\n } catch (error) {\n this.ErrorMessage = 'Failed to load app configuration. Please try again.';\n LogError('Error loading app configuration:', undefined, error instanceof Error ? error.message : String(error));\n } finally {\n this.ngZone.run(() => {\n this.IsLoading = false;\n this.cdr.detectChanges();\n });\n }\n }\n\n private buildAppConfigItems(systemApps: BaseApplication[], userApps: MJUserApplicationEntity[]): AppConfigItem[] {\n return systemApps.map(app => {\n const userApp = userApps.find(ua => UUIDsEqual(ua.ApplicationID, app.ID));\n return {\n app,\n userAppId: userApp?.ID ?? null,\n sequence: userApp?.Sequence ?? 999,\n isActive: userApp?.IsActive ?? false,\n isDirty: false\n };\n });\n }\n\n private refreshAppLists(): void {\n this.ActiveApps = this.AllApps\n .filter(item => item.isActive)\n .sort((a, b) => a.sequence - b.sequence);\n\n this.AvailableApps = this.AllApps\n .filter(item => !item.isActive)\n .sort((a, b) => a.app.Name.localeCompare(b.app.Name));\n }\n\n private resequenceActiveApps(): void {\n this.ActiveApps.forEach((item, index) => {\n if (item.sequence !== index) {\n item.sequence = index;\n item.isDirty = true;\n }\n });\n }\n\n private swapSequences(a: AppConfigItem, b: AppConfigItem): void {\n const tempSeq = a.sequence;\n a.sequence = b.sequence;\n b.sequence = tempSeq;\n a.isDirty = true;\n b.isDirty = true;\n }\n\n private async updateUserApplication(md: IMetadataProvider, item: AppConfigItem): Promise<void> {\n const userApp = await md.GetEntityObject<MJUserApplicationEntity>('MJ: User Applications');\n await userApp.Load(item.userAppId!);\n\n userApp.Sequence = item.sequence;\n userApp.IsActive = item.isActive;\n\n const saved = await userApp.Save();\n if (!saved) {\n throw new Error(`Failed to update UserApplication for ${item.app.Name}: ${userApp.LatestResult}`);\n }\n\n item.isDirty = false;\n LogStatus(`Updated UserApplication for ${item.app.Name}: sequence=${item.sequence}, isActive=${item.isActive}`);\n }\n\n private async createUserApplication(md: IMetadataProvider, item: AppConfigItem): Promise<void> {\n const userApp = await md.GetEntityObject<MJUserApplicationEntity>('MJ: User Applications');\n userApp.NewRecord();\n\n userApp.UserID = md.CurrentUser.ID;\n userApp.ApplicationID = item.app.ID;\n userApp.Sequence = item.sequence;\n userApp.IsActive = item.isActive;\n\n const saved = await userApp.Save();\n if (!saved) {\n throw new Error(`Failed to create UserApplication for ${item.app.Name}: ${userApp.LatestResult}`);\n }\n\n item.userAppId = userApp.ID;\n item.isDirty = false;\n LogStatus(`Created UserApplication for ${item.app.Name}: sequence=${item.sequence}`);\n }\n}\n","<!-- Modal Backdrop -->\n@if (ShowDialog) {\n <div class=\"modal-backdrop\" (click)=\"Close()\" role=\"presentation\"></div>\n <div\n class=\"modal-dialog full-screen-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"app-config-title\"\n >\n <!-- Dialog Header -->\n <div class=\"modal-header\">\n <h2 class=\"modal-title\" id=\"app-config-title\">\n <i class=\"fa-solid fa-grid-2\" aria-hidden=\"true\"></i>\n Configure Apps\n </h2>\n <button\n class=\"modal-close\"\n (click)=\"Close()\"\n aria-label=\"Close dialog\"\n >\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <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 <div class=\"error-message\" role=\"alert\">\n <i class=\"fa-solid fa-circle-exclamation\" aria-hidden=\"true\"></i>\n <span>{{ ErrorMessage }}</span>\n </div>\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 <div class=\"empty-state\">\n <i class=\"fa-solid fa-check-double\" aria-hidden=\"true\"></i>\n <p>All applications added!</p>\n </div>\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.background-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 <div class=\"empty-state\">\n <i class=\"fa-solid fa-inbox\" aria-hidden=\"true\"></i>\n <p>No applications selected</p>\n <p class=\"hint\">Add apps from the available list</p>\n </div>\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\" [style.border-left-color]=\"item.app.GetColor()\">\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.background-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=\"danger\" size=\"sm\"\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.background-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.background-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)=\"Close()\"\n [disabled]=\"IsSaving\"\n >\n Cancel\n </button>\n </div>\n </div>\n </div>\n </div>\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-explorer-settings",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.34.1",
|
|
4
4
|
"description": "MemberJunction: Reusable Angular components for the settings section of the MJ Explorer App",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -36,24 +36,24 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@angular/cdk": "21.1.3",
|
|
39
|
-
"@memberjunction/core": "5.
|
|
40
|
-
"@memberjunction/core-entities": "5.
|
|
41
|
-
"@memberjunction/global": "5.
|
|
42
|
-
"@memberjunction/graphql-dataprovider": "5.
|
|
43
|
-
"@memberjunction/ng-base-application": "5.
|
|
44
|
-
"@memberjunction/ng-base-forms": "5.
|
|
45
|
-
"@memberjunction/ng-base-types": "5.
|
|
46
|
-
"@memberjunction/ng-code-editor": "5.
|
|
47
|
-
"@memberjunction/ng-entity-form-dialog": "5.
|
|
48
|
-
"@memberjunction/ng-entity-permissions": "5.
|
|
49
|
-
"@memberjunction/ng-join-grid": "5.
|
|
50
|
-
"@memberjunction/ng-notifications": "5.
|
|
51
|
-
"@memberjunction/ng-shared": "5.
|
|
52
|
-
"@memberjunction/ng-shared-generic": "5.
|
|
53
|
-
"@memberjunction/ng-simple-record-list": "5.
|
|
54
|
-
"@memberjunction/ng-tabstrip": "5.
|
|
55
|
-
"@memberjunction/ng-ui-components": "5.
|
|
56
|
-
"@memberjunction/ng-user-avatar": "5.
|
|
39
|
+
"@memberjunction/core": "5.34.1",
|
|
40
|
+
"@memberjunction/core-entities": "5.34.1",
|
|
41
|
+
"@memberjunction/global": "5.34.1",
|
|
42
|
+
"@memberjunction/graphql-dataprovider": "5.34.1",
|
|
43
|
+
"@memberjunction/ng-base-application": "5.34.1",
|
|
44
|
+
"@memberjunction/ng-base-forms": "5.34.1",
|
|
45
|
+
"@memberjunction/ng-base-types": "5.34.1",
|
|
46
|
+
"@memberjunction/ng-code-editor": "5.34.1",
|
|
47
|
+
"@memberjunction/ng-entity-form-dialog": "5.34.1",
|
|
48
|
+
"@memberjunction/ng-entity-permissions": "5.34.1",
|
|
49
|
+
"@memberjunction/ng-join-grid": "5.34.1",
|
|
50
|
+
"@memberjunction/ng-notifications": "5.34.1",
|
|
51
|
+
"@memberjunction/ng-shared": "5.34.1",
|
|
52
|
+
"@memberjunction/ng-shared-generic": "5.34.1",
|
|
53
|
+
"@memberjunction/ng-simple-record-list": "5.34.1",
|
|
54
|
+
"@memberjunction/ng-tabstrip": "5.34.1",
|
|
55
|
+
"@memberjunction/ng-ui-components": "5.34.1",
|
|
56
|
+
"@memberjunction/ng-user-avatar": "5.34.1",
|
|
57
57
|
"rxjs": "~7.8.2",
|
|
58
58
|
"tslib": "^2.8.1"
|
|
59
59
|
},
|