@memberjunction/ng-explorer-core 5.49.0 → 5.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/omnibar/omnibar-palette.component.js +98 -96
- package/dist/lib/omnibar/omnibar-palette.component.js.map +1 -1
- package/dist/lib/resource-wrappers/view-resource.component.d.ts +19 -16
- package/dist/lib/resource-wrappers/view-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/view-resource.component.js +127 -121
- package/dist/lib/resource-wrappers/view-resource.component.js.map +1 -1
- package/dist/lib/shell/components/header/app-nav.component.d.ts +84 -9
- package/dist/lib/shell/components/header/app-nav.component.d.ts.map +1 -1
- package/dist/lib/shell/components/header/app-nav.component.js +380 -41
- package/dist/lib/shell/components/header/app-nav.component.js.map +1 -1
- package/dist/lib/shell/components/header/app-switcher.component.d.ts +159 -29
- package/dist/lib/shell/components/header/app-switcher.component.d.ts.map +1 -1
- package/dist/lib/shell/components/header/app-switcher.component.js +822 -116
- package/dist/lib/shell/components/header/app-switcher.component.js.map +1 -1
- package/dist/lib/shell/services/sharing-center-dialog-host.component.d.ts +10 -1
- package/dist/lib/shell/services/sharing-center-dialog-host.component.d.ts.map +1 -1
- package/dist/lib/shell/services/sharing-center-dialog-host.component.js +30 -4
- package/dist/lib/shell/services/sharing-center-dialog-host.component.js.map +1 -1
- package/dist/lib/shell/shell.component.d.ts +3 -0
- package/dist/lib/shell/shell.component.d.ts.map +1 -1
- package/dist/lib/shell/shell.component.js +22 -12
- package/dist/lib/shell/shell.component.js.map +1 -1
- package/dist/lib/shell/shell.module.d.ts +1 -1
- package/dist/lib/shell/shell.module.d.ts.map +1 -1
- package/dist/lib/shell/shell.module.js +3 -1
- package/dist/lib/shell/shell.module.js.map +1 -1
- package/package.json +51 -51
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-nav.component.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/header/app-nav.component.ts","../../../../../src/lib/shell/components/header/app-nav.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core';\nimport { BaseApplication, DynamicNavItem, NavItem, WorkspaceStateManager, WorkspaceConfiguration } from '@memberjunction/ng-base-application';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { Subject, takeUntil } from 'rxjs';\n\n/**\n * Event emitted when a nav item is clicked\n */\nexport interface NavItemClickEvent {\n item: NavItem;\n shiftKey: boolean;\n}\n\n/**\n * Horizontal navigation items for the current app.\n * Uses OnPush change detection and reactive state management for optimal performance.\n */\n@Component({\n standalone: false,\n selector: 'mj-app-nav',\n templateUrl: './app-nav.component.html',\n styleUrls: ['./app-nav.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AppNavComponent implements OnInit, OnDestroy {\n private destroy$ = new Subject<void>();\n private _app: BaseApplication | null = null;\n private _cachedNavItems: NavItem[] = [];\n private _cachedAppColor: string = 'var(--mj-brand-primary)';\n private _servicesInjected = false;\n\n /**\n * Monotonically increasing counter used to detect and discard stale async results.\n *\n * Because GetNavItems() is async (HomeApplication does a DB lookup for record names),\n * and RxJS subscribe() does NOT serialize async callbacks, multiple calls to\n * updateCachedData() can overlap. Without this guard, a slow call (e.g., Home app\n * doing a DB lookup) that started BEFORE a fast call (e.g., switching to App B)\n * could resolve AFTER the fast call and overwrite the correct nav items with stale ones.\n *\n * How it works:\n * 1. Each updateCachedData() call increments this counter and captures it as `gen`\n * 2. After the await, it checks: does `gen` still match `_updateGeneration`?\n * 3. If not, a newer call started while we were waiting — discard our stale results\n */\n private _updateGeneration = 0;\n\n // Map of nav item key (Route or Label) to active state\n private activeStateMap = new Map<string, boolean>();\n\n @Output() navItemClick = new EventEmitter<NavItemClickEvent>();\n @Output() navItemDismiss = new EventEmitter<NavItem>();\n\n constructor(\n private workspaceManager: WorkspaceStateManager,\n private sharedService: SharedService,\n private cdr: ChangeDetectorRef\n ) {}\n\n /**\n * Input setter for app - triggers cache update when app changes\n */\n @Input()\n set app(value: BaseApplication | null) {\n if (this._app !== value) {\n this._app = value;\n this._cachedNavItems = []; // Clear stale items immediately so previous app's items don't flash\n this.activeStateMap.clear();\n this._servicesInjected = false; // Reset injection flag\n this.updateCachedData();\n this.cdr.markForCheck();\n }\n }\n\n get app(): BaseApplication | null {\n return this._app;\n }\n\n ngOnInit(): void {\n // Subscribe to workspace configuration changes.\n // Must rebuild nav items (not just active states) because dynamic nav items\n // are generated based on the currently active tab - when a user navigates\n // from one record to another (e.g., via OpenEntityRecord), the active tab\n // changes and the dynamic nav item needs to reflect the new record.\n this.workspaceManager.Configuration\n .pipe(takeUntil(this.destroy$))\n .subscribe(async () => {\n await this.updateCachedData();\n });\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * Update cached nav items and app color when app changes\n */\n private async updateCachedData(): Promise<void> {\n // Capture the current generation before any async work.\n // See _updateGeneration JSDoc for full explanation of the race condition this prevents.\n const gen = ++this._updateGeneration;\n\n if (this._app) {\n // Inject services once for apps that need them (e.g., HomeApplication for dynamic nav items)\n if (!this._servicesInjected) {\n const appWithServices = this._app as BaseApplication & {\n SetWorkspaceManager?: (manager: WorkspaceStateManager) => void;\n SetSharedService?: (service: SharedService) => void;\n };\n\n if (typeof appWithServices.SetWorkspaceManager === 'function') {\n appWithServices.SetWorkspaceManager(this.workspaceManager);\n }\n if (typeof appWithServices.SetSharedService === 'function') {\n appWithServices.SetSharedService(this.sharedService);\n }\n this._servicesInjected = true;\n }\n\n const items = await this._app.GetNavItems() || [];\n\n // If a newer call started while we were awaiting, our results are stale — bail out\n // so we don't overwrite the newer call's (correct) results.\n if (gen !== this._updateGeneration) {\n return;\n }\n\n // Only show items with Status 'Active' or undefined (default to Active)\n this._cachedNavItems = items.filter(item => !item.Status || item.Status === 'Active');\n\n this._cachedAppColor = this._app.GetColor() || 'var(--mj-brand-primary)';\n } else {\n this._cachedNavItems = [];\n this._cachedAppColor = 'var(--mj-brand-primary)';\n }\n\n // Update active states after nav items change\n const config = this.workspaceManager.GetConfiguration();\n this.updateActiveStates(config);\n this.cdr.markForCheck();\n\n // In Angular 21 zoneless mode, markForCheck() alone is unreliable when the trigger\n // is an RxJS subscription (workspaceManager.Configuration here) not tracked by the\n // zoneless scheduler — the dirty flag is set but no follow-up tick is scheduled.\n // detectChanges() runs CD synchronously on this view, rendering the new data\n // immediately. Wrapped because detectChanges throws if invoked re-entrantly during\n // another in-flight CD pass — harmless if so.\n try {\n this.cdr.detectChanges();\n } catch {\n // Re-entrant CD — harmless, the in-flight pass picks up our markForCheck.\n }\n }\n\n /**\n * Update active state map based on current workspace configuration\n */\n private updateActiveStates(config: WorkspaceConfiguration | null): void {\n this.activeStateMap.clear();\n\n if (!config || !this._app) {\n return;\n }\n\n const activeTab = config.tabs.find(t => t.id === config.activeTabId);\n if (!activeTab || activeTab.applicationId !== this._app.ID) {\n return;\n }\n\n // Compute active state for each nav item once\n for (const item of this._cachedNavItems) {\n const key = this.getItemKey(item);\n const isActive = this.computeIsActive(item, activeTab);\n this.activeStateMap.set(key, isActive);\n }\n }\n\n /**\n * Get unique key for nav item (used for tracking and active state).\n * Prefers RecordID for dynamic items to avoid label collisions.\n */\n private getItemKey(item: NavItem): string {\n return item.RecordID || item.Route || item.Label || '';\n }\n\n /**\n * Check if a nav item is dynamic (generated from recent orphan resources)\n */\n isDynamic(item: NavItem): boolean {\n return (item as DynamicNavItem).isDynamic === true;\n }\n\n /**\n * Compute if nav item is active based on active tab\n */\n private computeIsActive(item: NavItem, activeTab: any): boolean {\n // Check if nav item has a custom matching function (for dynamic items)\n const dynamicItem = item as NavItem & { isActiveMatch?: (tab: unknown) => boolean };\n if (dynamicItem.isActiveMatch && typeof dynamicItem.isActiveMatch === 'function') {\n return dynamicItem.isActiveMatch(activeTab);\n }\n\n const config = activeTab.configuration || {};\n\n // Match by DriverClass (most reliable for Custom resource types — always set correctly)\n if (item.DriverClass && (config['driverClass'] === item.DriverClass || config['resourceTypeDriverClass'] === item.DriverClass)) {\n return true;\n }\n\n // Match by navItemName from config (reliable — set when nav item opens)\n if (config['navItemName'] && config['navItemName'] === item.Label) {\n return true;\n }\n\n // Match by route (for route-based nav items)\n if (item.Route && config['route'] === item.Route) {\n return true;\n }\n\n // NOTE: We intentionally do NOT match by activeTab.title here.\n // Tab titles can be stale (updated asynchronously by DisplayNameChangedEvent\n // from cached components) and cause double-matches where two nav items\n // both appear active. DriverClass and navItemName are sufficient.\n return false;\n }\n\n /**\n * Get cached navigation items (no computation in getter)\n */\n get navItems(): NavItem[] {\n return this._cachedNavItems;\n }\n\n /**\n * Get cached app color (no computation in getter)\n */\n get appColor(): string {\n return this._cachedAppColor;\n }\n\n /**\n * Check if nav item is active (uses cached state from Map)\n */\n isActive(item: NavItem): boolean {\n const key = this.getItemKey(item);\n return this.activeStateMap.get(key) || false;\n }\n\n /**\n * Track function for @for to optimize rendering\n */\n trackByNavItem(_index: number, item: NavItem): string {\n return this.getItemKey(item);\n }\n\n /**\n * Handle nav item click\n */\n onNavClick(item: NavItem, event?: MouseEvent): void {\n this.navItemClick.emit({\n item,\n shiftKey: event?.shiftKey || false\n });\n }\n\n /**\n * Handle dismiss click on a dynamic nav item.\n * Removes from the app's recent stack and refreshes nav items immediately.\n * Stops propagation so the nav click handler doesn't fire.\n */\n onDismiss(item: NavItem, event: MouseEvent): void {\n event.stopPropagation();\n\n // Remove from the app's recent stack directly so we can refresh immediately\n if (this._app) {\n const appWithRemove = this._app as BaseApplication & {\n RemoveDynamicNavItem?: (navItem: NavItem) => void;\n };\n if (typeof appWithRemove.RemoveDynamicNavItem === 'function') {\n appWithRemove.RemoveDynamicNavItem(item);\n this.updateCachedData();\n }\n }\n\n this.navItemDismiss.emit(item);\n }\n\n}\n","<nav class=\"nav-list\" [style.--app-color]=\"appColor\">\n @for (item of navItems; track trackByNavItem($index, item)) {\n <div\n class=\"nav-item\"\n [mjClickable]=\"item.Label\"\n role=\"link\"\n [testId]=\"'nav-item-' + item.Label\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\"\n [class.active]=\"isActive(item)\"\n [class.dynamic]=\"isDynamic(item)\"\n [class.no-icon]=\"!item.Icon\"\n (click)=\"onNavClick(item, $event)\">\n @if (item.Icon) {\n <i [class]=\"item.Icon\"></i>\n }\n <span>{{ item.Label }}</span>\n @if (item.Badge) {\n <span class=\"badge\">{{ item.Badge }}</span>\n }\n @if (isDynamic(item)) {\n <button class=\"dismiss-btn\" title=\"Remove\" (click)=\"onDismiss(item, $event)\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n }\n </div>\n }\n</nav>\n"]}
|
|
1
|
+
{"version":3,"file":"app-nav.component.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/header/app-nav.component.ts","../../../../../src/lib/shell/components/header/app-nav.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, AfterViewInit, ChangeDetectorRef, ChangeDetectionStrategy, ElementRef, ViewChild, HostListener } from '@angular/core';\nimport { BaseApplication, DynamicNavItem, NavItem, WorkspaceStateManager, WorkspaceConfiguration } from '@memberjunction/ng-base-application';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { Subject, takeUntil } from 'rxjs';\n\n/**\n * Event emitted when a nav item is clicked\n */\nexport interface NavItemClickEvent {\n item: NavItem;\n shiftKey: boolean;\n}\n\n/**\n * Horizontal navigation items for the current app.\n * Uses OnPush change detection and reactive state management for optimal performance.\n *\n * Overflow behavior (priority+ pattern): when the header can't fit every nav item,\n * trailing items collapse into a \"More\" dropdown instead of squeezing the header's\n * action cluster off-screen. Item widths are measured once per nav-items change\n * (all items render for one frame, widths are cached), then every resize recomputes\n * the fit from the cache — no re-measure, no layout thrash.\n */\n@Component({\n standalone: false,\n selector: 'mj-app-nav',\n templateUrl: './app-nav.component.html',\n styleUrls: ['./app-nav.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AppNavComponent implements OnInit, OnDestroy, AfterViewInit {\n private destroy$ = new Subject<void>();\n private _app: BaseApplication | null = null;\n private _cachedNavItems: NavItem[] = [];\n private _servicesInjected = false;\n\n /**\n * Monotonically increasing counter used to detect and discard stale async results.\n *\n * Because GetNavItems() is async (HomeApplication does a DB lookup for record names),\n * and RxJS subscribe() does NOT serialize async callbacks, multiple calls to\n * updateCachedData() can overlap. Without this guard, a slow call (e.g., Home app\n * doing a DB lookup) that started BEFORE a fast call (e.g., switching to App B)\n * could resolve AFTER the fast call and overwrite the correct nav items with stale ones.\n *\n * How it works:\n * 1. Each updateCachedData() call increments this counter and captures it as `gen`\n * 2. After the await, it checks: does `gen` still match `_updateGeneration`?\n * 3. If not, a newer call started while we were waiting — discard our stale results\n */\n private _updateGeneration = 0;\n\n // Map of nav item key (Route or Label) to active state\n private activeStateMap = new Map<string, boolean>();\n\n // ---- Overflow (priority+) state ----\n /** Cached natural pixel width of each nav item, parallel to navItems order */\n private itemWidths: number[] = [];\n /** Measured width of the More button (fallback used until first render) */\n private moreBtnWidth = 120;\n /** Left offset (px, relative to host) where the More dropdown anchors */\n public MoreDropdownLeft = 0;\n /** True when even one item + More can't fit — the lone visible pill\n * shrinks with an ellipsis instead of clipping the More button. */\n public Tight = false;\n /** Flex gap between nav items — measured from the rendered row (falls back\n * to --mj-space-1's default of 4px until first measurement) */\n private itemGap = 4;\n /** How many leading nav items are currently visible inline */\n public VisibleCount = Number.MAX_SAFE_INTEGER;\n /** Whether the More dropdown is open */\n public MoreOpen = false;\n /** True while nav items are being (re)loaded for skeleton display */\n public Loading = false;\n\n private resizeObserver: ResizeObserver | null = null;\n private _overflowEnabled = true;\n\n @ViewChild('navList') private navListRef?: ElementRef<HTMLElement>;\n @ViewChild('moreBtn') private moreBtnRef?: ElementRef<HTMLElement>;\n\n @Output() navItemClick = new EventEmitter<NavItemClickEvent>();\n @Output() navItemDismiss = new EventEmitter<NavItem>();\n\n /**\n * Disable the priority+ overflow behavior. Used by the mobile drawer instance,\n * where items stack vertically and can never overflow horizontally.\n */\n @Input()\n set OverflowEnabled(value: boolean) {\n this._overflowEnabled = value;\n if (!value) {\n this.VisibleCount = Number.MAX_SAFE_INTEGER;\n }\n }\n get OverflowEnabled(): boolean {\n return this._overflowEnabled;\n }\n\n constructor(\n private workspaceManager: WorkspaceStateManager,\n private sharedService: SharedService,\n private cdr: ChangeDetectorRef,\n private host: ElementRef<HTMLElement>\n ) {}\n\n /**\n * Input setter for app - triggers cache update when app changes\n */\n @Input()\n set app(value: BaseApplication | null) {\n if (this._app !== value) {\n this._app = value;\n this._cachedNavItems = []; // Clear stale items immediately so previous app's items don't flash\n this.activeStateMap.clear();\n this._servicesInjected = false; // Reset injection flag\n this.Loading = value != null; // Skeleton until GetNavItems resolves\n this.updateCachedData();\n this.cdr.markForCheck();\n }\n }\n\n get app(): BaseApplication | null {\n return this._app;\n }\n\n ngOnInit(): void {\n // Subscribe to workspace configuration changes.\n // Must rebuild nav items (not just active states) because dynamic nav items\n // are generated based on the currently active tab - when a user navigates\n // from one record to another (e.g., via OpenEntityRecord), the active tab\n // changes and the dynamic nav item needs to reflect the new record.\n this.workspaceManager.Configuration\n .pipe(takeUntil(this.destroy$))\n .subscribe(async () => {\n await this.updateCachedData();\n });\n }\n\n ngAfterViewInit(): void {\n if (this._overflowEnabled && typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(() => this.recomputeFit());\n this.resizeObserver.observe(this.host.nativeElement);\n }\n // Widths measured before webfonts arrive are stale (Explorer's Montserrat\n // loads late over the fallback stack) — invalidate the cache once fonts\n // settle so the fit is computed against real glyph metrics.\n if (this._overflowEnabled && typeof document !== 'undefined' && document.fonts?.ready) {\n document.fonts.ready.then(() => {\n this.itemWidths = [];\n this.recomputeFit();\n });\n }\n }\n\n ngOnDestroy(): void {\n this.resizeObserver?.disconnect();\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * Update cached nav items and app color when app changes\n */\n private async updateCachedData(): Promise<void> {\n // Capture the current generation before any async work.\n // See _updateGeneration JSDoc for full explanation of the race condition this prevents.\n const gen = ++this._updateGeneration;\n\n if (this._app) {\n // Inject services once for apps that need them (e.g., HomeApplication for dynamic nav items)\n if (!this._servicesInjected) {\n const appWithServices = this._app as BaseApplication & {\n SetWorkspaceManager?: (manager: WorkspaceStateManager) => void;\n SetSharedService?: (service: SharedService) => void;\n };\n\n if (typeof appWithServices.SetWorkspaceManager === 'function') {\n appWithServices.SetWorkspaceManager(this.workspaceManager);\n }\n if (typeof appWithServices.SetSharedService === 'function') {\n appWithServices.SetSharedService(this.sharedService);\n }\n this._servicesInjected = true;\n }\n\n const items = await this._app.GetNavItems() || [];\n\n // If a newer call started while we were awaiting, our results are stale — bail out\n // so we don't overwrite the newer call's (correct) results.\n if (gen !== this._updateGeneration) {\n return;\n }\n\n // Only show items with Status 'Active' or undefined (default to Active)\n this._cachedNavItems = items.filter(item => !item.Status || item.Status === 'Active');\n } else {\n this._cachedNavItems = [];\n }\n\n this.Loading = false;\n\n // Update active states after nav items change\n const config = this.workspaceManager.GetConfiguration();\n this.updateActiveStates(config);\n\n // New item set: show everything for one frame so natural widths can be\n // measured (the row clips, so the temporary full row is invisible),\n // then collapse to fit. Tight mode MUST be off during measurement — its\n // flex-shrink would record squeezed widths and poison the fit arithmetic.\n this.VisibleCount = Number.MAX_SAFE_INTEGER;\n this.MoreOpen = false;\n this.Tight = false;\n this.syncChangeDetection();\n\n if (this._overflowEnabled) {\n requestAnimationFrame(() => {\n this.measureItemWidths();\n this.recomputeFit();\n });\n }\n }\n\n /**\n * Cache the natural width of every nav item. Called in the frame after a new\n * item set renders (all items visible, flex-shrink: 0, so widths are natural\n * even if the row is clipped by overflow: hidden).\n */\n private measureItemWidths(): void {\n const list = this.navListRef?.nativeElement;\n if (!list) {\n return;\n }\n const els = Array.from(list.querySelectorAll<HTMLElement>('.nav-item:not(.nav-more-btn)'));\n if (els.length !== this._cachedNavItems.length) {\n return; // View out of sync (mid-CD) — the next update cycle re-measures\n }\n this.itemWidths = els.map(el => el.offsetWidth);\n // Measure the real flex gap (rem-based tokens can differ from the 4px\n // fallback if the root font-size changes)\n const gap = parseFloat(getComputedStyle(list).columnGap);\n if (!Number.isNaN(gap)) {\n this.itemGap = gap;\n }\n }\n\n /**\n * Decide how many items fit in the host's current width; the rest collapse\n * into the More dropdown. Pure arithmetic over cached widths — safe to call\n * on every resize.\n */\n private recomputeFit(): void {\n if (!this._overflowEnabled || this._cachedNavItems.length === 0) {\n return;\n }\n const hostWidth = this.host.nativeElement.clientWidth;\n if (hostWidth === 0) {\n return; // Hidden (mobile breakpoint) — leave state alone\n }\n if (!this.ensureFreshWidthCache()) {\n return; // Cache still stale — a retry frame is already scheduled\n }\n if (this.moreBtnRef?.nativeElement) {\n this.moreBtnWidth = this.moreBtnRef.nativeElement.offsetWidth || this.moreBtnWidth;\n }\n const fit = this.computeFitCount(hostWidth);\n this.applyFit(fit.count, fit.tight);\n }\n\n /**\n * Self-heal for the item-width cache: if it's stale or missing (the\n * measurement frame can race the first render after a reload/app switch),\n * expand to the full item set, re-measure, and retry next frame until it\n * converges. Returns true when the cache matches the current item set.\n */\n private ensureFreshWidthCache(): boolean {\n if (this.itemWidths.length === this._cachedNavItems.length) {\n return true;\n }\n if (this.VisibleCount < this._cachedNavItems.length || this.Tight) {\n this.VisibleCount = Number.MAX_SAFE_INTEGER;\n this.Tight = false; // Never measure with tight-mode shrink applied\n this.syncChangeDetection();\n }\n this.measureItemWidths();\n if (this.itemWidths.length !== this._cachedNavItems.length) {\n requestAnimationFrame(() => this.recomputeFit());\n return false;\n }\n return true;\n }\n\n /** Pure fit arithmetic: how many leading items fit beside the More button */\n private computeFitCount(hostWidth: number): { count: number; tight: boolean } {\n const gap = this.itemGap;\n const n = this.itemWidths.length;\n const totalAll = this.itemWidths.reduce((a, b) => a + b, 0) + gap * (n - 1);\n if (totalAll <= hostWidth) {\n return { count: Number.MAX_SAFE_INTEGER, tight: false }; // Everything fits\n }\n // Reserve room for the More button, then fit as many leading items as possible\n let used = this.moreBtnWidth;\n let fit = 0;\n for (let i = 0; i < n; i++) {\n const next = used + gap + this.itemWidths[i];\n if (next > hostWidth) {\n break;\n }\n used = next;\n fit++;\n }\n // Never collapse below one visible item; tight = even the lone item + More overflows\n return { count: Math.max(1, fit), tight: fit === 0 };\n }\n\n /** Commit a fit result, closing More if nothing overflows; CD only on change */\n private applyFit(count: number, tight: boolean): void {\n if (count === this.VisibleCount && tight === this.Tight) {\n return;\n }\n this.VisibleCount = count;\n this.Tight = tight;\n if (this.OverflowItems.length === 0) {\n this.MoreOpen = false;\n }\n this.syncChangeDetection();\n }\n\n /**\n * markForCheck + synchronous detectChanges. In Angular 21 zoneless mode,\n * markForCheck() alone is unreliable when the trigger is an RxJS\n * subscription or rAF callback not tracked by the zoneless scheduler — the\n * dirty flag is set but no follow-up tick is scheduled. detectChanges()\n * throws if invoked re-entrantly during an in-flight CD pass; that's\n * harmless (the in-flight pass picks up our markForCheck), hence the catch.\n */\n private syncChangeDetection(): void {\n this.cdr.markForCheck();\n try {\n this.cdr.detectChanges();\n } catch {\n // Re-entrant CD — harmless.\n }\n }\n\n /** Items rendered inline in the header row */\n get InlineItems(): NavItem[] {\n return this._cachedNavItems.slice(0, this.VisibleCount);\n }\n\n /** Items collapsed into the More dropdown */\n get OverflowItems(): NavItem[] {\n return this.VisibleCount >= this._cachedNavItems.length\n ? []\n : this._cachedNavItems.slice(this.VisibleCount);\n }\n\n /** True when any overflowed item is the active one (More button shows active tint) */\n get OverflowHasActive(): boolean {\n return this.OverflowItems.some(item => this.isActive(item));\n }\n\n /** Open/close the More dropdown, anchoring it under the More button and clamping it inside the host */\n ToggleMore(event: MouseEvent): void {\n event.stopPropagation();\n this.MoreOpen = !this.MoreOpen;\n if (this.MoreOpen) {\n // Anchor the dropdown under the More button; once the dropdown has\n // rendered, clamp it so its right edge stays inside the host.\n this.MoreDropdownLeft = this.moreBtnRef?.nativeElement?.offsetLeft ?? 0;\n requestAnimationFrame(() => {\n const dropdown = this.host.nativeElement.querySelector<HTMLElement>('.nav-more-dropdown');\n if (dropdown) {\n const maxLeft = this.host.nativeElement.clientWidth - dropdown.offsetWidth;\n this.MoreDropdownLeft = Math.max(0, Math.min(this.MoreDropdownLeft, maxLeft));\n this.cdr.markForCheck();\n try {\n this.cdr.detectChanges();\n } catch {\n // Re-entrant CD — harmless.\n }\n }\n });\n }\n this.cdr.markForCheck();\n }\n\n /** Close the More dropdown on any outside click or Escape */\n @HostListener('document:click', ['$event'])\n onDocumentClick(event: MouseEvent): void {\n if (this.MoreOpen && !this.host.nativeElement.contains(event.target as Node)) {\n this.MoreOpen = false;\n this.cdr.markForCheck();\n }\n }\n\n /** Close the More dropdown on Escape */\n @HostListener('document:keydown.escape')\n onEscape(): void {\n if (this.MoreOpen) {\n this.MoreOpen = false;\n this.cdr.markForCheck();\n }\n }\n\n /**\n * Update active state map based on current workspace configuration\n */\n private updateActiveStates(config: WorkspaceConfiguration | null): void {\n this.activeStateMap.clear();\n\n if (!config || !this._app) {\n return;\n }\n\n const activeTab = config.tabs.find(t => t.id === config.activeTabId);\n if (!activeTab || activeTab.applicationId !== this._app.ID) {\n return;\n }\n\n // Compute active state for each nav item once\n for (const item of this._cachedNavItems) {\n const key = this.getItemKey(item);\n const isActive = this.computeIsActive(item, activeTab);\n this.activeStateMap.set(key, isActive);\n }\n }\n\n /**\n * Get unique key for nav item (used for tracking and active state).\n * Prefers RecordID for dynamic items to avoid label collisions.\n */\n private getItemKey(item: NavItem): string {\n return item.RecordID || item.Route || item.Label || '';\n }\n\n /**\n * Check if a nav item is dynamic (generated from recent orphan resources)\n */\n isDynamic(item: NavItem): boolean {\n return (item as DynamicNavItem).isDynamic === true;\n }\n\n /**\n * Compute if nav item is active based on active tab\n */\n private computeIsActive(item: NavItem, activeTab: any): boolean {\n // Check if nav item has a custom matching function (for dynamic items)\n const dynamicItem = item as NavItem & { isActiveMatch?: (tab: unknown) => boolean };\n if (dynamicItem.isActiveMatch && typeof dynamicItem.isActiveMatch === 'function') {\n return dynamicItem.isActiveMatch(activeTab);\n }\n\n const config = activeTab.configuration || {};\n\n // Match by DriverClass (most reliable for Custom resource types — always set correctly)\n if (item.DriverClass && (config['driverClass'] === item.DriverClass || config['resourceTypeDriverClass'] === item.DriverClass)) {\n return true;\n }\n\n // Match by navItemName from config (reliable — set when nav item opens)\n if (config['navItemName'] && config['navItemName'] === item.Label) {\n return true;\n }\n\n // Match by route (for route-based nav items)\n if (item.Route && config['route'] === item.Route) {\n return true;\n }\n\n // NOTE: We intentionally do NOT match by activeTab.title here.\n // Tab titles can be stale (updated asynchronously by DisplayNameChangedEvent\n // from cached components) and cause double-matches where two nav items\n // both appear active. DriverClass and navItemName are sufficient.\n return false;\n }\n\n /**\n * Get cached navigation items (no computation in getter)\n */\n get navItems(): NavItem[] {\n return this._cachedNavItems;\n }\n\n /**\n * Check if nav item is active (uses cached state from Map)\n */\n isActive(item: NavItem): boolean {\n const key = this.getItemKey(item);\n return this.activeStateMap.get(key) || false;\n }\n\n /**\n * Track function for @for to optimize rendering\n */\n trackByNavItem(_index: number, item: NavItem): string {\n return this.getItemKey(item);\n }\n\n /**\n * Handle nav item click\n */\n onNavClick(item: NavItem, event?: MouseEvent): void {\n this.MoreOpen = false;\n this.navItemClick.emit({\n item,\n shiftKey: event?.shiftKey || false\n });\n }\n\n /**\n * Handle dismiss click on a dynamic nav item.\n * Removes from the app's recent stack and refreshes nav items immediately.\n * Stops propagation so the nav click handler doesn't fire.\n */\n onDismiss(item: NavItem, event: MouseEvent): void {\n event.stopPropagation();\n\n // Remove from the app's recent stack directly so we can refresh immediately\n if (this._app) {\n const appWithRemove = this._app as BaseApplication & {\n RemoveDynamicNavItem?: (navItem: NavItem) => void;\n };\n if (typeof appWithRemove.RemoveDynamicNavItem === 'function') {\n appWithRemove.RemoveDynamicNavItem(item);\n this.updateCachedData();\n }\n }\n\n this.navItemDismiss.emit(item);\n }\n\n}\n","<nav class=\"nav-list\" #navList [class.tight]=\"Tight\">\n @if (Loading && navItems.length === 0) {\n <!-- Skeleton pills while GetNavItems() resolves — prevents header pop-in -->\n <div class=\"nav-skeleton\" aria-hidden=\"true\">\n <span class=\"nav-skeleton-pill\" style=\"width: 88px\"></span>\n <span class=\"nav-skeleton-pill\" style=\"width: 72px\"></span>\n <span class=\"nav-skeleton-pill\" style=\"width: 96px\"></span>\n </div>\n }\n @for (item of InlineItems; track trackByNavItem($index, item)) {\n <div\n class=\"nav-item\"\n [mjClickable]=\"item.Label\"\n role=\"link\"\n [testId]=\"'nav-item-' + item.Label\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\"\n [class.active]=\"isActive(item)\"\n [class.dynamic]=\"isDynamic(item)\"\n [class.no-icon]=\"!item.Icon\"\n (click)=\"onNavClick(item, $event)\">\n @if (item.Icon) {\n <i [class]=\"item.Icon\"></i>\n }\n <span>{{ item.Label }}</span>\n @if (item.Badge) {\n <span class=\"badge\">{{ item.Badge }}</span>\n }\n @if (isDynamic(item)) {\n <button class=\"dismiss-btn\" title=\"Remove\" (click)=\"onDismiss(item, $event)\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n }\n </div>\n }\n @if (OverflowItems.length > 0) {\n <div\n #moreBtn\n class=\"nav-item nav-more-btn\"\n mjClickable=\"More navigation items\"\n testId=\"nav-more-btn\"\n [attr.aria-haspopup]=\"true\"\n [attr.aria-expanded]=\"MoreOpen\"\n [class.active]=\"OverflowHasActive\"\n (click)=\"ToggleMore($event)\">\n <i class=\"fa-solid fa-ellipsis\"></i>\n <span>More</span>\n <span class=\"more-count\">{{ OverflowItems.length }}</span>\n </div>\n }\n</nav>\n\n<!-- The dropdown lives OUTSIDE the clipped nav row (the row keeps\n overflow: hidden so fit-arithmetic slack can never paint over the\n header's action cluster). Anchored to the More button by measurement. -->\n@if (MoreOpen && OverflowItems.length > 0) {\n <div class=\"nav-more-dropdown\" [style.left.px]=\"MoreDropdownLeft\">\n @for (item of OverflowItems; track trackByNavItem($index, item)) {\n <div\n class=\"nav-more-item\"\n [mjClickable]=\"item.Label\"\n role=\"link\"\n [testId]=\"'nav-more-item-' + item.Label\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\"\n [class.active]=\"isActive(item)\"\n [class.dynamic]=\"isDynamic(item)\"\n (click)=\"onNavClick(item, $event)\">\n @if (item.Icon) {\n <i [class]=\"item.Icon\"></i>\n }\n <span class=\"more-item-label\">{{ item.Label }}</span>\n @if (item.Badge) {\n <span class=\"badge\">{{ item.Badge }}</span>\n }\n @if (isDynamic(item)) {\n <button class=\"dismiss-btn\" title=\"Remove\" (click)=\"onDismiss(item, $event)\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n }\n </div>\n }\n </div>\n}\n"]}
|
|
@@ -1,62 +1,192 @@
|
|
|
1
|
-
import { EventEmitter } from '@angular/core';
|
|
1
|
+
import { EventEmitter, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
|
|
2
2
|
import { ApplicationManager, BaseApplication } from '@memberjunction/ng-base-application';
|
|
3
|
-
import {
|
|
3
|
+
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
4
4
|
import * as i0 from "@angular/core";
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Presentation style for the app switcher, resolved from the
|
|
7
|
+
* `Shell.AppSwitcher.Style` instance-config key:
|
|
8
|
+
* - 'launcher': always the centered card launcher
|
|
9
|
+
* - 'compact': always the anchored compact panel (dropdown-like)
|
|
10
|
+
* - 'auto' (default): compact below {@link COMPACT_AUTO_THRESHOLD} apps
|
|
8
11
|
*/
|
|
9
|
-
export
|
|
12
|
+
export type AppSwitcherStyle = 'launcher' | 'compact' | 'auto';
|
|
13
|
+
/**
|
|
14
|
+
* App switcher in the header: the trigger shows the active app's identity
|
|
15
|
+
* glyph; activating it opens a centered launcher overlay — a filterable grid
|
|
16
|
+
* of app cards (identity glyph + name + Description summary), with the user's
|
|
17
|
+
* most recently used apps surfaced first.
|
|
18
|
+
*
|
|
19
|
+
* Rendering model: the panel is a native <dialog> opened with showModal(),
|
|
20
|
+
* so it lives in the browser's TOP LAYER — above every stacking context in
|
|
21
|
+
* the app (the shell header's z-index:500 context, chat FAB, toasts). The
|
|
22
|
+
* modal state also makes the rest of the document inert (focus cannot
|
|
23
|
+
* escape) and the browser restores focus to the trigger on close.
|
|
24
|
+
*
|
|
25
|
+
* Accessibility model: every app card is a real focusable control (via
|
|
26
|
+
* mjClickable), so Tab walks filter → close → cards → Configure, and
|
|
27
|
+
* Enter/Space activate the focused card. Arrow keys are the fast path:
|
|
28
|
+
* ArrowDown from the filter focuses the first card; arrows move between
|
|
29
|
+
* cards; ArrowUp from the first card returns to the filter. Escape layers:
|
|
30
|
+
* discard-bar → config view → filter → close. Exiting the config view with
|
|
31
|
+
* unsaved changes shows an inline discard bar (a body-appended confirm
|
|
32
|
+
* dialog would be inert/beneath the top layer).
|
|
33
|
+
*/
|
|
34
|
+
export declare class AppSwitcherComponent extends BaseAngularComponent implements OnInit, OnDestroy {
|
|
10
35
|
private appManager;
|
|
36
|
+
private cdr;
|
|
37
|
+
private destroy$;
|
|
11
38
|
activeApp: BaseApplication | null;
|
|
12
39
|
isViewingSystemTab: boolean;
|
|
13
40
|
/** ID of the app currently being loaded (shows loading indicator) */
|
|
14
41
|
loadingAppId: string | null;
|
|
42
|
+
/** Presentation style (from Shell.AppSwitcher.Style instance config) */
|
|
43
|
+
SwitcherStyle: AppSwitcherStyle;
|
|
15
44
|
appSelected: EventEmitter<string>;
|
|
16
|
-
|
|
45
|
+
private filterInputRef?;
|
|
46
|
+
private triggerRef?;
|
|
47
|
+
private panelRef?;
|
|
48
|
+
private configContent?;
|
|
17
49
|
showDropdown: boolean;
|
|
18
|
-
|
|
19
|
-
|
|
50
|
+
/** When true, the launcher body shows the app-configuration view instead of the card grid */
|
|
51
|
+
ConfigMode: boolean;
|
|
52
|
+
/** Pending action awaiting the inline unsaved-changes discard bar ('exit' = back to grid, 'close' = close launcher) */
|
|
53
|
+
PendingDiscard: 'exit' | 'close' | null;
|
|
54
|
+
/** True when the panel renders as the compact anchored dropdown (few apps) */
|
|
55
|
+
CompactMode: boolean;
|
|
56
|
+
/** All-apps sort: the user's configured order ('custom') or A–Z ('alpha').
|
|
57
|
+
* Per-user preference, persisted via UserInfoEngine. */
|
|
58
|
+
SortMode: 'custom' | 'alpha';
|
|
59
|
+
/** Anchor position for the compact panel (px, from the trigger's rect) */
|
|
60
|
+
AnchorLeft: number;
|
|
61
|
+
AnchorTop: number;
|
|
62
|
+
/** Live filter text — matches app names AND Description summaries */
|
|
63
|
+
FilterText: string;
|
|
64
|
+
private recentEntries;
|
|
65
|
+
constructor(appManager: ApplicationManager, cdr: ChangeDetectorRef);
|
|
66
|
+
/**
|
|
67
|
+
* UserInfoEngine scoped to this component's provider (multi-provider rule:
|
|
68
|
+
* never reach for the global singleton in per-provider code paths). For the
|
|
69
|
+
* default single-provider shell this resolves to the already-loaded engine
|
|
70
|
+
* registered under the default connection. When no provider is available at
|
|
71
|
+
* all (test environments), fall back to the global singleton rather than
|
|
72
|
+
* letting GetProviderInstance construct a fresh unloaded engine.
|
|
73
|
+
*/
|
|
74
|
+
private get userInfoEngine();
|
|
75
|
+
ngOnInit(): void;
|
|
76
|
+
ngOnDestroy(): void;
|
|
77
|
+
/**
|
|
78
|
+
* Re-resolve the presentation for the CURRENT app count. No-op while the
|
|
79
|
+
* config view is open (the panel is promoted to full size there anyway);
|
|
80
|
+
* called when the app stream emits and when the config view exits.
|
|
81
|
+
*/
|
|
82
|
+
RefreshPresentation(): void;
|
|
83
|
+
/** Template handler for the filter input's input event */
|
|
84
|
+
OnFilterInput(event: Event): void;
|
|
20
85
|
/**
|
|
21
86
|
* Check if the app switcher should show loading state
|
|
22
87
|
*/
|
|
23
88
|
get isLoading(): boolean;
|
|
24
89
|
/**
|
|
25
|
-
*
|
|
26
|
-
* (
|
|
90
|
+
* Visible trigger label: the current app's name, or "Apps" when there's no
|
|
91
|
+
* app context (system tabs, nothing active yet).
|
|
27
92
|
*/
|
|
28
|
-
get
|
|
93
|
+
get TriggerLabel(): string;
|
|
94
|
+
/** Accessible name for the trigger — names the control AND the current app */
|
|
95
|
+
get TriggerAriaLabel(): string;
|
|
29
96
|
/**
|
|
30
|
-
*
|
|
97
|
+
* Get applications that should appear in the app switcher
|
|
98
|
+
* (NavigationStyle = 'App Switcher' or 'Both')
|
|
31
99
|
*/
|
|
32
|
-
|
|
100
|
+
get apps(): BaseApplication[];
|
|
101
|
+
/** Recently-used apps (persisted per user), newest first, capped for display.
|
|
102
|
+
* Compact mode skips the Recent section — with a handful of apps it's noise. */
|
|
103
|
+
get RecentApps(): BaseApplication[];
|
|
104
|
+
/** All switcher apps in the user's chosen sort (custom Sequence order or A–Z) */
|
|
105
|
+
private get sortedApps();
|
|
106
|
+
/** The complete "All apps" section, in the user's chosen sort. Apps shown in
|
|
107
|
+
* Recent appear here TOO — being recently used must not make an app vanish
|
|
108
|
+
* from its expected place in the full list. */
|
|
109
|
+
get OtherApps(): BaseApplication[];
|
|
110
|
+
/** Flat visible list in RENDERED card order — the keyboard-navigation index
|
|
111
|
+
* space. Filtering collapses the sections into one deduplicated list;
|
|
112
|
+
* otherwise it's Recent followed by the full All-apps list (a recent app
|
|
113
|
+
* intentionally appears twice, matching the two cards on screen). */
|
|
114
|
+
get VisibleApps(): BaseApplication[];
|
|
115
|
+
/** Distinct app count for the screen-reader status (VisibleApps can contain
|
|
116
|
+
* the same app twice when it's in both Recent and All apps) */
|
|
117
|
+
get AnnouncedAppCount(): number;
|
|
118
|
+
/** True when a filter is active (sections collapse into one result list) */
|
|
119
|
+
get IsFiltering(): boolean;
|
|
120
|
+
/** Compact mode hides the filter under a handful of apps — it's noise there */
|
|
121
|
+
get ShowFilter(): boolean;
|
|
122
|
+
private applyFilter;
|
|
123
|
+
/** Stable DOM id for a card (arrow-key focus target) */
|
|
124
|
+
OptionId(index: number): string;
|
|
125
|
+
/** Trigger handler: open the launcher panel, or close it if already open */
|
|
126
|
+
ToggleLauncher(): void;
|
|
127
|
+
/** Flip the All-apps sort and persist it (fail-silent, like recents) */
|
|
128
|
+
ToggleSortMode(): void;
|
|
129
|
+
private loadSortMode;
|
|
130
|
+
private openLauncher;
|
|
131
|
+
/** Resolve the presentation for this open (auto = compact under the threshold) */
|
|
132
|
+
private resolveCompactMode;
|
|
133
|
+
/** Anchor the compact panel under the trigger, clamped to the viewport */
|
|
134
|
+
private computeCompactAnchor;
|
|
135
|
+
private closeLauncher;
|
|
136
|
+
/** Guarded close for template paths (backdrop / close button): unsaved
|
|
137
|
+
* config changes surface the inline discard bar instead of closing. */
|
|
138
|
+
CloseLauncher(): void;
|
|
139
|
+
/** Backdrop clicks arrive as clicks on the <dialog> element itself */
|
|
140
|
+
OnDialogClick(event: MouseEvent): void;
|
|
141
|
+
/** Native cancel (Esc while the browser owns the dialog) — route through
|
|
142
|
+
* our layered Escape handling instead of closing unconditionally. */
|
|
143
|
+
OnDialogCancel(event: Event): void;
|
|
144
|
+
/** Inline discard bar: confirm — perform the pending action, dropping changes */
|
|
145
|
+
ConfirmDiscard(): void;
|
|
146
|
+
/** Inline discard bar: keep editing */
|
|
147
|
+
CancelDiscard(): void;
|
|
148
|
+
OnFilterChange(): void;
|
|
149
|
+
/** Keyboard model on the filter input */
|
|
150
|
+
OnFilterKeydown(event: KeyboardEvent): void;
|
|
151
|
+
/** Arrow-key navigation between focused cards */
|
|
152
|
+
OnCardKeydown(index: number, event: KeyboardEvent): void;
|
|
153
|
+
/** Move DOM focus to the card at the given VisibleApps index (clamped) */
|
|
154
|
+
private focusCard;
|
|
155
|
+
/** Keep Tab cycling within the launcher's real focusables (filter, close, Configure) */
|
|
156
|
+
/** Escape layering: discard bar → config view (guarded) → filter → close.
|
|
157
|
+
* Tab is NOT manually trapped — showModal() makes the background inert,
|
|
158
|
+
* which is a real trap (no leak when focus sits on a non-focusable). */
|
|
159
|
+
OnPanelKeydown(event: KeyboardEvent): void;
|
|
160
|
+
private handleEscape;
|
|
33
161
|
/**
|
|
34
|
-
* Select an application
|
|
35
|
-
* When viewing a system tab, always emit to allow returning to the app
|
|
162
|
+
* Select an application.
|
|
163
|
+
* When viewing a system tab, always emit to allow returning to the app.
|
|
36
164
|
*/
|
|
37
165
|
selectApp(app: BaseApplication): void;
|
|
38
166
|
/** Case-insensitive UUID check whether an app is the currently active app. */
|
|
39
167
|
IsActiveApp(app: BaseApplication): boolean;
|
|
40
168
|
/** Case-insensitive UUID check whether an app is the one currently loading. */
|
|
41
169
|
IsLoadingApp(app: BaseApplication): boolean;
|
|
170
|
+
private loadRecents;
|
|
171
|
+
private recordRecent;
|
|
42
172
|
/**
|
|
43
|
-
*
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Open the app configuration dialog
|
|
173
|
+
* Swap the launcher body to the app-configuration view (in-panel, seamless —
|
|
174
|
+
* no separate dialog). The embedded mj-user-app-config-content loads on
|
|
175
|
+
* creation.
|
|
48
176
|
*/
|
|
49
|
-
|
|
177
|
+
EnterConfigMode(): void;
|
|
178
|
+
/** Guarded exit from the config view: unsaved changes surface the inline
|
|
179
|
+
* discard bar instead of silently dropping the user's edits. */
|
|
180
|
+
RequestExitConfigMode(): void;
|
|
50
181
|
/**
|
|
51
|
-
*
|
|
52
|
-
* The app list refreshes reactively: each UserApplication save
|
|
53
|
-
* event, UserInfoEngine's debounced refresh
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* which the `apps` getter reads. No explicit reload needed here.
|
|
182
|
+
* Return from the configuration view to the app grid.
|
|
183
|
+
* The app list refreshes reactively after a save: each UserApplication save
|
|
184
|
+
* fires a BaseEntity event, UserInfoEngine's debounced refresh emits
|
|
185
|
+
* DataChange$, and ApplicationManager.syncFromEngine() pushes the new list
|
|
186
|
+
* to applications$, which the `apps` getter reads. No explicit reload needed.
|
|
57
187
|
*/
|
|
58
|
-
|
|
188
|
+
ExitConfigMode(): void;
|
|
59
189
|
static ɵfac: i0.ɵɵFactoryDeclaration<AppSwitcherComponent, never>;
|
|
60
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<AppSwitcherComponent, "mj-app-switcher", never, { "activeApp": { "alias": "activeApp"; "required": false; }; "isViewingSystemTab": { "alias": "isViewingSystemTab"; "required": false; }; "loadingAppId": { "alias": "loadingAppId"; "required": false; }; }, { "appSelected": "appSelected"; }, never, never, false, never>;
|
|
190
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<AppSwitcherComponent, "mj-app-switcher", never, { "activeApp": { "alias": "activeApp"; "required": false; }; "isViewingSystemTab": { "alias": "isViewingSystemTab"; "required": false; }; "loadingAppId": { "alias": "loadingAppId"; "required": false; }; "SwitcherStyle": { "alias": "SwitcherStyle"; "required": false; }; }, { "appSelected": "appSelected"; }, never, never, false, never>;
|
|
61
191
|
}
|
|
62
192
|
//# sourceMappingURL=app-switcher.component.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-switcher.component.d.ts","sourceRoot":"","sources":["../../../../../src/lib/shell/components/header/app-switcher.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,
|
|
1
|
+
{"version":3,"file":"app-switcher.component.d.ts","sourceRoot":"","sources":["../../../../../src/lib/shell/components/header/app-switcher.component.ts"],"names":[],"mappings":"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"}
|