@memberjunction/ng-explorer-core 6.2.0-edge.3 → 6.2.0-edge.5
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/README.md +10 -0
- package/dist/app-routing.module.d.ts.map +1 -1
- package/dist/app-routing.module.js +13 -6
- package/dist/app-routing.module.js.map +1 -1
- package/dist/generated/lazy-feature-config.d.ts +1 -1
- package/dist/generated/lazy-feature-config.d.ts.map +1 -1
- package/dist/generated/lazy-feature-config.js +4 -2
- package/dist/generated/lazy-feature-config.js.map +1 -1
- package/dist/lib/guards/moved-nav-item-redirect.d.ts +15 -0
- package/dist/lib/guards/moved-nav-item-redirect.d.ts.map +1 -0
- package/dist/lib/guards/moved-nav-item-redirect.js +37 -0
- package/dist/lib/guards/moved-nav-item-redirect.js.map +1 -0
- package/dist/lib/oauth/oauth-callback.component.d.ts +2 -0
- package/dist/lib/oauth/oauth-callback.component.d.ts.map +1 -1
- package/dist/lib/oauth/oauth-callback.component.js +10 -10
- package/dist/lib/oauth/oauth-callback.component.js.map +1 -1
- package/dist/lib/omnibar/omnibar-dashboard-matches.d.ts +34 -0
- package/dist/lib/omnibar/omnibar-dashboard-matches.d.ts.map +1 -0
- package/dist/lib/omnibar/omnibar-dashboard-matches.js +70 -0
- package/dist/lib/omnibar/omnibar-dashboard-matches.js.map +1 -0
- package/dist/lib/omnibar/omnibar-palette.component.d.ts +27 -2
- package/dist/lib/omnibar/omnibar-palette.component.d.ts.map +1 -1
- package/dist/lib/omnibar/omnibar-palette.component.js +126 -65
- package/dist/lib/omnibar/omnibar-palette.component.js.map +1 -1
- package/dist/lib/omnibar/omnibar-provider.d.ts +4 -0
- package/dist/lib/omnibar/omnibar-provider.d.ts.map +1 -1
- package/dist/lib/omnibar/omnibar-provider.js.map +1 -1
- package/dist/lib/omnibar/providers/omnibar-record.provider.d.ts +2 -2
- package/dist/lib/omnibar/providers/omnibar-record.provider.d.ts.map +1 -1
- package/dist/lib/omnibar/providers/omnibar-record.provider.js +3 -7
- package/dist/lib/omnibar/providers/omnibar-record.provider.js.map +1 -1
- package/dist/lib/omnibar/providers/omnibar-search.provider.d.ts +15 -2
- package/dist/lib/omnibar/providers/omnibar-search.provider.d.ts.map +1 -1
- package/dist/lib/omnibar/providers/omnibar-search.provider.js +59 -7
- package/dist/lib/omnibar/providers/omnibar-search.provider.js.map +1 -1
- package/dist/lib/profile/forking-setting.d.ts +8 -0
- package/dist/lib/profile/forking-setting.d.ts.map +1 -0
- package/dist/lib/profile/forking-setting.js +18 -0
- package/dist/lib/profile/forking-setting.js.map +1 -0
- package/dist/lib/profile/profile-dialog.component.d.ts +8 -0
- package/dist/lib/profile/profile-dialog.component.d.ts.map +1 -1
- package/dist/lib/profile/profile-dialog.component.js +182 -92
- package/dist/lib/profile/profile-dialog.component.js.map +1 -1
- package/dist/lib/resource-wrappers/artifact-resource.component.d.ts +16 -1
- package/dist/lib/resource-wrappers/artifact-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/artifact-resource.component.js +27 -4
- package/dist/lib/resource-wrappers/artifact-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/chat-conversations-resource.component.d.ts +24 -21
- package/dist/lib/resource-wrappers/chat-conversations-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/chat-conversations-resource.component.js +231 -54
- package/dist/lib/resource-wrappers/chat-conversations-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/chat-search-routing.d.ts +10 -3
- package/dist/lib/resource-wrappers/chat-search-routing.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/chat-search-routing.js +11 -4
- package/dist/lib/resource-wrappers/chat-search-routing.js.map +1 -1
- package/dist/lib/resource-wrappers/chat-tasks-resource.component.d.ts +12 -3
- package/dist/lib/resource-wrappers/chat-tasks-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/chat-tasks-resource.component.js +18 -3
- package/dist/lib/resource-wrappers/chat-tasks-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/dashboard-add-to-menu.component.d.ts +29 -0
- package/dist/lib/resource-wrappers/dashboard-add-to-menu.component.d.ts.map +1 -0
- package/dist/lib/resource-wrappers/dashboard-add-to-menu.component.js +141 -0
- package/dist/lib/resource-wrappers/dashboard-add-to-menu.component.js.map +1 -0
- package/dist/lib/resource-wrappers/dashboard-part-config.d.ts +28 -0
- package/dist/lib/resource-wrappers/dashboard-part-config.d.ts.map +1 -0
- package/dist/lib/resource-wrappers/dashboard-part-config.js +150 -0
- package/dist/lib/resource-wrappers/dashboard-part-config.js.map +1 -0
- package/dist/lib/resource-wrappers/dashboard-resource.component.d.ts +361 -56
- package/dist/lib/resource-wrappers/dashboard-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/dashboard-resource.component.js +1312 -361
- package/dist/lib/resource-wrappers/dashboard-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/dashboard-source-search.d.ts +86 -0
- package/dist/lib/resource-wrappers/dashboard-source-search.d.ts.map +1 -0
- package/dist/lib/resource-wrappers/dashboard-source-search.js +136 -0
- package/dist/lib/resource-wrappers/dashboard-source-search.js.map +1 -0
- package/dist/lib/resource-wrappers/dashboard-studio-pane.d.ts +30 -0
- package/dist/lib/resource-wrappers/dashboard-studio-pane.d.ts.map +1 -0
- package/dist/lib/resource-wrappers/dashboard-studio-pane.js +55 -0
- package/dist/lib/resource-wrappers/dashboard-studio-pane.js.map +1 -0
- package/dist/lib/resource-wrappers/dashboard-studio-tools.d.ts +135 -0
- package/dist/lib/resource-wrappers/dashboard-studio-tools.d.ts.map +1 -0
- package/dist/lib/resource-wrappers/dashboard-studio-tools.js +856 -0
- package/dist/lib/resource-wrappers/dashboard-studio-tools.js.map +1 -0
- package/dist/lib/resource-wrappers/dashboard-tab-agent.d.ts +87 -0
- package/dist/lib/resource-wrappers/dashboard-tab-agent.d.ts.map +1 -0
- package/dist/lib/resource-wrappers/dashboard-tab-agent.js +125 -0
- package/dist/lib/resource-wrappers/dashboard-tab-agent.js.map +1 -0
- package/dist/lib/resource-wrappers/livekit-room-resource.component.d.ts +28 -7
- package/dist/lib/resource-wrappers/livekit-room-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/livekit-room-resource.component.js +174 -112
- package/dist/lib/resource-wrappers/livekit-room-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/search-results-resource.component.d.ts +3 -3
- package/dist/lib/resource-wrappers/search-results-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/search-results-resource.component.js +10 -14
- package/dist/lib/resource-wrappers/search-results-resource.component.js.map +1 -1
- package/dist/lib/server-connectivity/server-connectivity-banner.component.d.ts +16 -3
- package/dist/lib/server-connectivity/server-connectivity-banner.component.d.ts.map +1 -1
- package/dist/lib/server-connectivity/server-connectivity-banner.component.js +17 -3
- package/dist/lib/server-connectivity/server-connectivity-banner.component.js.map +1 -1
- package/dist/lib/shell/components/tabs/tab-container.component.d.ts +41 -0
- package/dist/lib/shell/components/tabs/tab-container.component.d.ts.map +1 -1
- package/dist/lib/shell/components/tabs/tab-container.component.js +170 -39
- package/dist/lib/shell/components/tabs/tab-container.component.js.map +1 -1
- package/dist/lib/shell/dashboard-tab-for-url.d.ts +35 -0
- package/dist/lib/shell/dashboard-tab-for-url.d.ts.map +1 -0
- package/dist/lib/shell/dashboard-tab-for-url.js +39 -0
- package/dist/lib/shell/dashboard-tab-for-url.js.map +1 -0
- package/dist/lib/shell/deep-link-workspace.d.ts +25 -0
- package/dist/lib/shell/deep-link-workspace.d.ts.map +1 -0
- package/dist/lib/shell/deep-link-workspace.js +49 -0
- package/dist/lib/shell/deep-link-workspace.js.map +1 -0
- package/dist/lib/shell/shell.component.d.ts +43 -2
- package/dist/lib/shell/shell.component.d.ts.map +1 -1
- package/dist/lib/shell/shell.component.js +174 -42
- package/dist/lib/shell/shell.component.js.map +1 -1
- package/dist/lib/shell/shell.module.d.ts +2 -1
- package/dist/lib/shell/shell.module.d.ts.map +1 -1
- package/dist/lib/shell/shell.module.js +6 -2
- package/dist/lib/shell/shell.module.js.map +1 -1
- package/dist/lib/single-dashboard/single-dashboard.component.js +1 -1
- package/dist/lib/single-dashboard/single-dashboard.component.js.map +1 -1
- package/dist/lib/single-record/single-record.component.d.ts +12 -0
- package/dist/lib/single-record/single-record.component.d.ts.map +1 -1
- package/dist/lib/single-record/single-record.component.js +49 -8
- package/dist/lib/single-record/single-record.component.js.map +1 -1
- package/dist/lib/template-user/template-user-banner.component.d.ts +20 -0
- package/dist/lib/template-user/template-user-banner.component.d.ts.map +1 -0
- package/dist/lib/template-user/template-user-banner.component.js +67 -0
- package/dist/lib/template-user/template-user-banner.component.js.map +1 -0
- package/dist/module.d.ts +61 -59
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +7 -0
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +1 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +1 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +58 -53
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Component, ViewChild, runInInjectionContext, createComponent, ViewEncapsulation, HostListener, Output, EventEmitter, inject } from '@angular/core';
|
|
2
|
-
import { GoldenLayoutManager, FlattenLayoutToSingleStack } from '@memberjunction/ng-base-application';
|
|
2
|
+
import { GoldenLayoutManager, FlattenLayoutToSingleStack, TabService } from '@memberjunction/ng-base-application';
|
|
3
3
|
import { MJGlobal } from '@memberjunction/global';
|
|
4
|
-
import { BaseResourceComponent, HomeAppPinService, NavigationService, IsRecordTabsStyle, IsRecordsTabConfiguration, IsRecordsRegionTab, IsRecordDockedToWorkspace, RECORD_DOCKED_TO_WORKSPACE_KEY, GetRecordSourceContext, SafeDetectChanges, ExplorerBreakpointService, ResolveRecordTypeIcon } from '@memberjunction/ng-shared';
|
|
4
|
+
import { BaseResourceComponent, HomeAppPinService, NavigationService, IsRecordTabsStyle, IsRecordsTabConfiguration, IsRecordsRegionTab, IsRecordDockedToWorkspace, RECORD_DOCKED_TO_WORKSPACE_KEY, GetRecordSourceContext, SafeDetectChanges, ExplorerBreakpointService, ResolveRecordTypeIcon, GetDeletedRecordFromEvent, RecordTabShowsRecord, RemoveDeletedRecordFromOrigin, OriginToTabConfiguration, OriginHasReturnTarget, NormalizeRecordIdForMatch } from '@memberjunction/ng-shared';
|
|
5
5
|
import { ResourceData, ResourcePermissionEngine } from '@memberjunction/core-entities';
|
|
6
6
|
import { RecordOriginCrumbComponent } from '../record-open/record-origin-crumb.component';
|
|
7
7
|
import { RecordSwitcherService } from '../record-open/record-switcher.service';
|
|
@@ -141,6 +141,8 @@ function TabContainerComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
|
141
141
|
} }
|
|
142
142
|
/** Fallback tab accent when an app has no color (matches pre-existing usage) */
|
|
143
143
|
const DEFAULT_APP_COLOR = '#757575';
|
|
144
|
+
/** A single-field key in URL-segment form ('Field|value'); group 1 is the value. */
|
|
145
|
+
const SINGLE_FIELD_URL_SEGMENT = /^[^|]+\|([^|]+)$/;
|
|
144
146
|
/**
|
|
145
147
|
* Container for Golden Layout tabs with app-colored styling.
|
|
146
148
|
*
|
|
@@ -214,6 +216,7 @@ export class TabContainerComponent extends BaseAngularComponent {
|
|
|
214
216
|
layoutInitError = this.LayoutInitError;
|
|
215
217
|
pinService = inject(HomeAppPinService);
|
|
216
218
|
navigationService = inject(NavigationService);
|
|
219
|
+
tabService = inject(TabService);
|
|
217
220
|
recordSwitcher = inject(RecordSwitcherService);
|
|
218
221
|
subscriptions = [];
|
|
219
222
|
layoutInitRetryCount = 0;
|
|
@@ -471,6 +474,7 @@ export class TabContainerComponent extends BaseAngularComponent {
|
|
|
471
474
|
flushRegionCd() {
|
|
472
475
|
SafeDetectChanges(this.cdr);
|
|
473
476
|
}
|
|
477
|
+
recordsLayoutInitRetryCount = 0;
|
|
474
478
|
/**
|
|
475
479
|
* Keep the RECORDS region in lockstep with the workspace configuration:
|
|
476
480
|
* resolve region visibility (active tab is a record), lazily initialize the
|
|
@@ -491,6 +495,13 @@ export class TabContainerComponent extends BaseAngularComponent {
|
|
|
491
495
|
const recordTabs = config.tabs.filter(t => this.isRecordTab(t));
|
|
492
496
|
const activeTab = config.tabs.find(t => t.id === config.activeTabId);
|
|
493
497
|
const showing = !!activeTab && this.isRecordTab(activeTab);
|
|
498
|
+
if (showing !== this.ShowRecordsRegion) {
|
|
499
|
+
this.ShowRecordsRegion = showing;
|
|
500
|
+
// Flush NOW — the visibility class must land BEFORE records GL init/tab
|
|
501
|
+
// creation so synchronous TabShown events see ShowRecordsRegion=true and
|
|
502
|
+
// load the active record immediately instead of parking it.
|
|
503
|
+
this.flushRegionCd();
|
|
504
|
+
}
|
|
494
505
|
// EAGER init + sync on EVERY emission. The region container always keeps
|
|
495
506
|
// its full layout box (visibility-hidden, never display:none — see the
|
|
496
507
|
// component CSS), so the records GL can initialize and reconcile even
|
|
@@ -504,14 +515,8 @@ export class TabContainerComponent extends BaseAngularComponent {
|
|
|
504
515
|
if (this.recordsLayoutInitialized) {
|
|
505
516
|
this.syncRecordsTabs(recordTabs);
|
|
506
517
|
}
|
|
507
|
-
if (
|
|
508
|
-
this.
|
|
509
|
-
// Flush NOW — the visibility class must land in this pass, not
|
|
510
|
-
// whenever the next unrelated emission happens to run CD.
|
|
511
|
-
this.flushRegionCd();
|
|
512
|
-
if (showing) {
|
|
513
|
-
this.flushPendingRecordShows();
|
|
514
|
-
}
|
|
518
|
+
if (this.ShowRecordsRegion) {
|
|
519
|
+
this.flushPendingRecordShows();
|
|
515
520
|
}
|
|
516
521
|
if (showing && activeTab && this.recordsLayoutInitialized) {
|
|
517
522
|
this.focusRecordsTab(activeTab.id);
|
|
@@ -734,17 +739,24 @@ export class TabContainerComponent extends BaseAngularComponent {
|
|
|
734
739
|
return;
|
|
735
740
|
}
|
|
736
741
|
const container = this.RecordsGlContainer?.nativeElement;
|
|
737
|
-
if (!container) {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
742
|
+
if (!container || container.getBoundingClientRect().height === 0) {
|
|
743
|
+
// NEVER initialize GL inside a missing or zero-size (hidden) container —
|
|
744
|
+
// it bakes a 0x0 internal size into every stack it later creates and the
|
|
745
|
+
// region renders collapsed forever. If the records region IS the active
|
|
746
|
+
// surface (e.g. startup deep link to a record before layout settles),
|
|
747
|
+
// schedule a bounded retry so initialization is not stranded.
|
|
748
|
+
if (this.ShowRecordsRegion && this.recordsLayoutInitRetryCount < this.MAX_LAYOUT_INIT_RETRIES) {
|
|
749
|
+
this.recordsLayoutInitRetryCount++;
|
|
750
|
+
setTimeout(() => {
|
|
751
|
+
const latest = this.workspaceManager.GetConfiguration();
|
|
752
|
+
if (latest && !this.recordsLayoutInitialized) {
|
|
753
|
+
this.syncRecordsRegion(latest);
|
|
754
|
+
}
|
|
755
|
+
}, 50);
|
|
756
|
+
}
|
|
746
757
|
return;
|
|
747
758
|
}
|
|
759
|
+
this.recordsLayoutInitRetryCount = 0;
|
|
748
760
|
// Mobile: headerless GL — the record bar replaces the strip
|
|
749
761
|
this.recordsLayoutManager.Initialize(container, { HideHeaders: this.mobileRecordsActive });
|
|
750
762
|
this.recordsLayoutInitialized = true;
|
|
@@ -763,6 +775,14 @@ export class TabContainerComponent extends BaseAngularComponent {
|
|
|
763
775
|
// loading such a layout would render a ghost pane for a closed record.
|
|
764
776
|
if (layoutToLoad?.root && recordTabs.length > 0 && this.layoutCoversExactTabSet(layoutToLoad.root, recordTabs)) {
|
|
765
777
|
if (this.recordsLayoutManager.LoadLayout(layoutToLoad)) {
|
|
778
|
+
recordTabs.forEach(tab => this.updateTabDisplayName(tab));
|
|
779
|
+
setTimeout(() => {
|
|
780
|
+
const latest = this.workspaceManager.GetConfiguration();
|
|
781
|
+
if (latest?.activeTabId && this.ShowRecordsRegion) {
|
|
782
|
+
this.focusRecordsTab(latest.activeTabId);
|
|
783
|
+
}
|
|
784
|
+
this.recordsLayoutManager.updateSize();
|
|
785
|
+
}, 50);
|
|
766
786
|
return;
|
|
767
787
|
}
|
|
768
788
|
}
|
|
@@ -872,6 +892,23 @@ export class TabContainerComponent extends BaseAngularComponent {
|
|
|
872
892
|
this.ShowContextMenu(event.x, event.y, event.tabId, event.anchorEl);
|
|
873
893
|
}));
|
|
874
894
|
this.wireRecordsLayoutEvents();
|
|
895
|
+
// A record deleted in this session: close every tab showing it and move the
|
|
896
|
+
// user off it (see HandleRecordDeleted). Remote deletes are left to the
|
|
897
|
+
// record tab itself, which shows a banner rather than vanishing under the user.
|
|
898
|
+
this.subscriptions.push(MJGlobal.Instance.GetEventListener().subscribe(event => {
|
|
899
|
+
// Read the key NOW: right after raising 'delete' the entity calls
|
|
900
|
+
// NewRecord() and its key is gone.
|
|
901
|
+
const deleted = GetDeletedRecordFromEvent(event);
|
|
902
|
+
if (!deleted || deleted.Remote) {
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
// Deferred one macrotask: the event fires inside BaseEntity.Delete(), and
|
|
906
|
+
// the deleting form still has to finish (success toast, RecordDeleted
|
|
907
|
+
// handlers) before its tab is torn down.
|
|
908
|
+
setTimeout(() => {
|
|
909
|
+
this.HandleRecordDeleted(deleted).catch(err => LogError(err));
|
|
910
|
+
}, 0);
|
|
911
|
+
}));
|
|
875
912
|
// Breakpoint crossings rebuild the records GL in the other chrome mode
|
|
876
913
|
// (headerless+flattened vs full strip+splits). Skip-if-equal: the
|
|
877
914
|
// BehaviorSubject replays the current value on subscribe.
|
|
@@ -1034,6 +1071,11 @@ export class TabContainerComponent extends BaseAngularComponent {
|
|
|
1034
1071
|
}));
|
|
1035
1072
|
}
|
|
1036
1073
|
ngAfterViewInit() {
|
|
1074
|
+
const config = this.workspaceManager.GetConfiguration();
|
|
1075
|
+
if (config) {
|
|
1076
|
+
this.syncRecordsRegion(config);
|
|
1077
|
+
this.updateRecordSurfaceState(config);
|
|
1078
|
+
}
|
|
1037
1079
|
// Initialize Golden Layout only if we're not in single-resource mode
|
|
1038
1080
|
if (!this.UseSingleResourceMode) {
|
|
1039
1081
|
this.initializeGoldenLayout();
|
|
@@ -1725,13 +1767,26 @@ export class TabContainerComponent extends BaseAngularComponent {
|
|
|
1725
1767
|
const config = this.workspaceManager.GetConfiguration();
|
|
1726
1768
|
const tab = config?.tabs.find(t => t.id === tabId);
|
|
1727
1769
|
const isLastTab = config?.tabs.length === 1 && config.tabs[0].id === tabId;
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1770
|
+
this.closeTabDestroyingComponent(tabId);
|
|
1771
|
+
if (isLastTab && tab) {
|
|
1772
|
+
// CloseTab kept the tab around but unpinned it. Replace it with the app's
|
|
1773
|
+
// default tab so the user lands on a meaningful surface (typically the
|
|
1774
|
+
// home dashboard) instead of staying on the discarded form.
|
|
1775
|
+
await this.openAppDefaultTab(tab.applicationId);
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
/**
|
|
1779
|
+
* Close a tab and DESTROY (not just detach) its cached component.
|
|
1780
|
+
*
|
|
1781
|
+
* The default tab-close path detaches and keeps components alive for reuse —
|
|
1782
|
+
* but a discarded or deleted record's form is exactly what we DON'T want to
|
|
1783
|
+
* keep: it holds a stale BaseEntity in view mode. The next "Create New Record"
|
|
1784
|
+
* click for the same entity would otherwise hit the cache discriminator
|
|
1785
|
+
* lookup, find this stale component, and reattach it — surfacing a blank form
|
|
1786
|
+
* in view mode instead of a fresh edit-mode form. Destroy here forces a cache
|
|
1787
|
+
* miss on the next open.
|
|
1788
|
+
*/
|
|
1789
|
+
closeTabDestroyingComponent(tabId) {
|
|
1735
1790
|
if (this.singleResourceCacheIdentity?.tabId === tabId) {
|
|
1736
1791
|
// Single-resource mode: the component is tracked via singleResource* fields,
|
|
1737
1792
|
// and its cache entry is currently marked attached. Use the identity directly
|
|
@@ -1742,7 +1797,7 @@ export class TabContainerComponent extends BaseAngularComponent {
|
|
|
1742
1797
|
this.singleResourceComponentRef = null;
|
|
1743
1798
|
this.singleResourceCacheIdentity = null;
|
|
1744
1799
|
// Clear the host container's DOM so the user isn't briefly looking at the
|
|
1745
|
-
// destroyed component's wrapper between CloseTab and the
|
|
1800
|
+
// destroyed component's wrapper between CloseTab and the next tab's load.
|
|
1746
1801
|
const directContainer = this.DirectContentContainer?.nativeElement;
|
|
1747
1802
|
if (directContainer) {
|
|
1748
1803
|
while (directContainer.firstChild)
|
|
@@ -1756,18 +1811,78 @@ export class TabContainerComponent extends BaseAngularComponent {
|
|
|
1756
1811
|
this.componentRefs.delete(tabId);
|
|
1757
1812
|
}
|
|
1758
1813
|
this.workspaceManager.CloseTab(tabId);
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1814
|
+
}
|
|
1815
|
+
/** Open an app's default tab (its home / landing surface), if it has one. */
|
|
1816
|
+
async openAppDefaultTab(appId) {
|
|
1817
|
+
const app = this.appManager.GetAppById(appId);
|
|
1818
|
+
if (app) {
|
|
1819
|
+
const defaultTabRequest = await app.CreateDefaultTab();
|
|
1820
|
+
if (defaultTabRequest) {
|
|
1821
|
+
this.workspaceManager.OpenTab(defaultTabRequest, app.GetColor());
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
/**
|
|
1826
|
+
* A record was deleted in this session — from its own form, a custom form, a
|
|
1827
|
+
* grid row, or any other `BaseEntity.Delete()`. Nothing may keep showing it:
|
|
1828
|
+
*
|
|
1829
|
+
* 1. Other record tabs whose origin chain names it get the record spliced out,
|
|
1830
|
+
* so their crumb returns to where the user was before it.
|
|
1831
|
+
* 2. Every record tab showing it closes, its cached form destroyed.
|
|
1832
|
+
* 3. If one of those was the ACTIVE tab, the user is taken back to where it
|
|
1833
|
+
* was opened from; with no origin (e.g. loaded straight from a URL), to the
|
|
1834
|
+
* Home dashboard, or the tab's own app landing page when there is no Home
|
|
1835
|
+
* app. Its URL's history entry is replaced so browser Back can't reopen it.
|
|
1836
|
+
*
|
|
1837
|
+
* Inactive tabs showing it just close: the user wasn't looking at them.
|
|
1838
|
+
*/
|
|
1839
|
+
async HandleRecordDeleted(record) {
|
|
1840
|
+
const config = this.workspaceManager.GetConfiguration();
|
|
1841
|
+
if (!config) {
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
const showing = config.tabs.filter(t => RecordTabShowsRecord(t, record));
|
|
1845
|
+
const showingIds = new Set(showing.map(t => t.id));
|
|
1846
|
+
for (const tab of config.tabs) {
|
|
1847
|
+
if (showingIds.has(tab.id) || !IsRecordsTabConfiguration(tab.configuration)) {
|
|
1848
|
+
continue;
|
|
1849
|
+
}
|
|
1850
|
+
const origin = GetRecordSourceContext(tab.configuration) ?? undefined;
|
|
1851
|
+
const pruned = RemoveDeletedRecordFromOrigin(origin, record);
|
|
1852
|
+
if (pruned !== origin) {
|
|
1853
|
+
this.workspaceManager.UpdateTabConfiguration(tab.id, OriginToTabConfiguration(pruned));
|
|
1769
1854
|
}
|
|
1770
1855
|
}
|
|
1856
|
+
if (showing.length === 0) {
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
const activeTab = showing.find(t => t.id === config.activeTabId);
|
|
1860
|
+
const returnTo = activeTab
|
|
1861
|
+
? RemoveDeletedRecordFromOrigin(GetRecordSourceContext(activeTab.configuration) ?? undefined, record)
|
|
1862
|
+
: undefined;
|
|
1863
|
+
if (activeTab) {
|
|
1864
|
+
// Mark by what the tab's URL carries (its own entity string and id), not
|
|
1865
|
+
// the event's canonical name: the URL is built from the tab.
|
|
1866
|
+
const urlEntity = activeTab.configuration?.['Entity'];
|
|
1867
|
+
this.tabService.MarkRecordUrlForReplacement(typeof urlEntity === 'string' ? urlEntity : record.EntityName, NormalizeRecordIdForMatch(activeTab.resourceRecordId || record.RecordId));
|
|
1868
|
+
}
|
|
1869
|
+
for (const tab of showing) {
|
|
1870
|
+
this.closeTabDestroyingComponent(tab.id);
|
|
1871
|
+
}
|
|
1872
|
+
if (activeTab) {
|
|
1873
|
+
await this.leaveDeletedRecord(activeTab, returnTo);
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
/** Where the user goes after the record they were looking at is deleted. */
|
|
1877
|
+
async leaveDeletedRecord(deletedTab, origin) {
|
|
1878
|
+
if (OriginHasReturnTarget(origin)) {
|
|
1879
|
+
await this.navigationService.ReturnToRecordSource(origin);
|
|
1880
|
+
return;
|
|
1881
|
+
}
|
|
1882
|
+
if (await this.navigationService.OpenHomeDashboard()) {
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
await this.openAppDefaultTab(deletedTab.applicationId);
|
|
1771
1886
|
}
|
|
1772
1887
|
/**
|
|
1773
1888
|
* Generate a signature for tab content to detect when content changes
|
|
@@ -2176,6 +2291,18 @@ export class TabContainerComponent extends BaseAngularComponent {
|
|
|
2176
2291
|
const normalize = (name) => (name ?? '').trim().toLowerCase().replace(/^mj:\s*/, '');
|
|
2177
2292
|
return normalize(a) === normalize(b);
|
|
2178
2293
|
}
|
|
2294
|
+
/**
|
|
2295
|
+
* Whether two resource record ids name the same record. A component's ResourceRecordSaved
|
|
2296
|
+
* stores a single-field key in the URL-segment form ('ID|<uuid>'), while its tab may keep the
|
|
2297
|
+
* bare value ('<uuid>'). The field name is dropped only when the other id is a bare value, so
|
|
2298
|
+
* two ids that both contain '|' are compared as they are.
|
|
2299
|
+
*/
|
|
2300
|
+
static IsSameRecordId(a, b) {
|
|
2301
|
+
const left = (a ?? '').trim();
|
|
2302
|
+
const right = (b ?? '').trim();
|
|
2303
|
+
const bareValue = (id) => SINGLE_FIELD_URL_SEGMENT.exec(id)?.[1] ?? id;
|
|
2304
|
+
return left === right || bareValue(left) === right || left === bareValue(right);
|
|
2305
|
+
}
|
|
2179
2306
|
async getResourceTypeId(resourceType) {
|
|
2180
2307
|
const rt = await this.getResourceTypeEntity(resourceType);
|
|
2181
2308
|
if (rt) {
|
|
@@ -2386,9 +2513,13 @@ export class TabContainerComponent extends BaseAngularComponent {
|
|
|
2386
2513
|
// reload from being handed back the component it just detached.
|
|
2387
2514
|
const existingEntity = existingResourceData?.Configuration?.Entity;
|
|
2388
2515
|
const newEntity = tab.configuration['Entity'];
|
|
2516
|
+
// The record id comparison accepts the URL-segment form ResourceRecordSaved writes into
|
|
2517
|
+
// the component's Data ('ID|<uuid>' for a tab that keeps '<uuid>'). An exact comparison
|
|
2518
|
+
// reloads the same cached component on every configuration emission, and each reload
|
|
2519
|
+
// emits a new configuration.
|
|
2389
2520
|
const needsReload = !TabContainerComponent.IsSameResourceType(existingResourceData?.ResourceType, tab.configuration['resourceType']) ||
|
|
2390
2521
|
existingResourceData?.Configuration?.applicationId !== tab.applicationId ||
|
|
2391
|
-
existingRecordId
|
|
2522
|
+
!TabContainerComponent.IsSameRecordId(existingRecordId, newRecordId) ||
|
|
2392
2523
|
existingEntity !== newEntity ||
|
|
2393
2524
|
(tab.configuration['resourceType'] === 'Custom' && existingDriverClass !== newDriverClass);
|
|
2394
2525
|
if (needsReload) {
|
|
@@ -2953,5 +3084,5 @@ export class TabContainerComponent extends BaseAngularComponent {
|
|
|
2953
3084
|
type: HostListener,
|
|
2954
3085
|
args: ['window:resize']
|
|
2955
3086
|
}] }); })();
|
|
2956
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TabContainerComponent, { className: "TabContainerComponent", filePath: "src/lib/shell/components/tabs/tab-container.component.ts", lineNumber:
|
|
3087
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TabContainerComponent, { className: "TabContainerComponent", filePath: "src/lib/shell/components/tabs/tab-container.component.ts", lineNumber: 68 }); })();
|
|
2957
3088
|
//# sourceMappingURL=tab-container.component.js.map
|