@memberjunction/ng-shared 5.51.0 → 6.1.0-edge.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.
- package/LICENSE +7 -0
- package/dist/lib/base-dashboard.d.ts +16 -0
- package/dist/lib/base-dashboard.d.ts.map +1 -1
- package/dist/lib/base-dashboard.js +36 -5
- package/dist/lib/base-dashboard.js.map +1 -1
- package/dist/lib/base-resource-component.d.ts +33 -3
- package/dist/lib/base-resource-component.d.ts.map +1 -1
- package/dist/lib/base-resource-component.js +65 -11
- package/dist/lib/base-resource-component.js.map +1 -1
- package/dist/lib/explorer-breakpoint.service.d.ts +39 -0
- package/dist/lib/explorer-breakpoint.service.d.ts.map +1 -0
- package/dist/lib/explorer-breakpoint.service.js +57 -0
- package/dist/lib/explorer-breakpoint.service.js.map +1 -0
- package/dist/lib/home-pin.service.d.ts.map +1 -1
- package/dist/lib/home-pin.service.js +0 -2
- package/dist/lib/home-pin.service.js.map +1 -1
- package/dist/lib/navigation.interfaces.d.ts +13 -0
- package/dist/lib/navigation.interfaces.d.ts.map +1 -1
- package/dist/lib/navigation.interfaces.js.map +1 -1
- package/dist/lib/navigation.service.d.ts +70 -8
- package/dist/lib/navigation.service.d.ts.map +1 -1
- package/dist/lib/navigation.service.js +356 -50
- package/dist/lib/navigation.service.js.map +1 -1
- package/dist/lib/record-open-style.d.ts +88 -0
- package/dist/lib/record-open-style.d.ts.map +1 -0
- package/dist/lib/record-open-style.js +124 -0
- package/dist/lib/record-open-style.js.map +1 -0
- package/dist/lib/record-tab-icon.d.ts +10 -0
- package/dist/lib/record-tab-icon.d.ts.map +1 -0
- package/dist/lib/record-tab-icon.js +15 -0
- package/dist/lib/record-tab-icon.js.map +1 -0
- package/dist/lib/safe-detect-changes.d.ts +18 -0
- package/dist/lib/safe-detect-changes.d.ts.map +1 -0
- package/dist/lib/safe-detect-changes.js +29 -0
- package/dist/lib/safe-detect-changes.js.map +1 -0
- package/dist/lib/shared.service.d.ts +5 -1
- package/dist/lib/shared.service.d.ts.map +1 -1
- package/dist/lib/shared.service.js +16 -14
- package/dist/lib/shared.service.js.map +1 -1
- package/dist/public-api.d.ts +4 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +4 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +22 -20
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 MemberJunction
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@@ -40,8 +40,24 @@ export declare abstract class BaseDashboard extends BaseResourceComponent implem
|
|
|
40
40
|
ngOnDestroy(): void;
|
|
41
41
|
/**
|
|
42
42
|
* This method will result in the dashboard being reloaded.
|
|
43
|
+
*
|
|
44
|
+
* NOTE — this promise does NOT reject when the reload fails. A load error is caught, logged via
|
|
45
|
+
* `LogError` and surfaced on the {@link Error} output (see {@link runGuardedLoad}), and the
|
|
46
|
+
* promise then RESOLVES. That is deliberate: callers invoke `Refresh()` fire-and-forget (the
|
|
47
|
+
* container does), so rejecting here would produce an unhandled rejection rather than reaching
|
|
48
|
+
* anyone. If you need to know whether a refresh succeeded, subscribe to {@link Error} — do not
|
|
49
|
+
* rely on `await Refresh()` throwing.
|
|
43
50
|
*/
|
|
44
51
|
Refresh(): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Runs a load phase (initial mount or Refresh) with the GUARANTEE that {@link NotifyLoadComplete}
|
|
54
|
+
* always fires — even if the work throws. The Explorer shell's loading screen blocks on that
|
|
55
|
+
* signal, so without this guard one dashboard's uncaught `initDashboard()`/`loadData()` error
|
|
56
|
+
* (e.g. an in-flight query rejecting while MJAPI restarts) would hang the entire shell forever on
|
|
57
|
+
* a direct/deep-link load. On error we log it and emit the {@link Error} output so the subclass or
|
|
58
|
+
* container can render its own error state; the loading screen still clears either way.
|
|
59
|
+
*/
|
|
60
|
+
private runGuardedLoad;
|
|
45
61
|
private _visible;
|
|
46
62
|
/**
|
|
47
63
|
* This method can be used by a container to let the dashboard know that it is being opened/closed. Base class just sets a flag.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-dashboard.d.ts","sourceRoot":"","sources":["../../src/lib/base-dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAS,SAAS,EAAE,MAAM,EAAU,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"base-dashboard.d.ts","sourceRoot":"","sources":["../../src/lib/base-dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAS,SAAS,EAAE,MAAM,EAAU,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,YAAY,EAAY,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AACxF,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;;AAElE,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,yBAAyB,CAAC;IACrC,SAAS,CAAC,EAAE,GAAG,CAAC;CACjB;AAED;;GAEG;AACH,8BACsB,aAAc,SAAQ,qBAAsB,YAAW,MAAM,EAAE,SAAS;IAC5F;;OAEG;IACH,IAAa,MAAM,CAAC,KAAK,EAAE,eAAe,EAEzC;IAED,IAAI,MAAM,IAAI,eAAe,GAAG,IAAI,CAEnC;IAED;;OAEG;IACO,KAAK,sBAA6B;IAE5C;;OAEG;IACO,gBAAgB,oBAA2B;IAErD;;OAEG;IACO,WAAW,oBAA2B;IAEhD;;OAEG;IACO,gBAAgB;oBAAiC,MAAM;oBAAc,YAAY;OAAK;IAEhG,SAAS,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAAQ;IAE3C,QAAQ;IAUd,WAAW,IAAI,IAAI;IAInB;;;;;;;;;OASG;IACU,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrC;;;;;;;OAOG;YACW,cAAc;IAY5B,OAAO,CAAC,QAAQ,CAAkB;IAClC;;OAEG;IACI,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIzC;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa,IAAI,IAAI;IAExC;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,QAAQ,IAAI,IAAI;IAEnC;;;;OAIG;IACG,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;yCAzG3C,aAAa;2CAAb,aAAa;CA4GlC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Directive, EventEmitter, Input, Output } from '@angular/core';
|
|
2
|
+
import { LogError } from '@memberjunction/core';
|
|
2
3
|
import { BaseResourceComponent } from './base-resource-component';
|
|
3
4
|
import * as i0 from "@angular/core";
|
|
4
5
|
/**
|
|
@@ -33,19 +34,49 @@ export class BaseDashboard extends BaseResourceComponent {
|
|
|
33
34
|
_config = null;
|
|
34
35
|
async ngOnInit() {
|
|
35
36
|
super.ngOnInit();
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
this.
|
|
37
|
+
// initDashboard() runs before loadData() and can throw too, so BOTH are inside the guard — a
|
|
38
|
+
// failure in either still reaches NotifyLoadComplete() (see runGuardedLoad).
|
|
39
|
+
await this.runGuardedLoad(async () => {
|
|
40
|
+
this.initDashboard();
|
|
41
|
+
await this.loadData();
|
|
42
|
+
});
|
|
39
43
|
}
|
|
40
44
|
ngOnDestroy() {
|
|
41
45
|
super.ngOnDestroy();
|
|
42
46
|
}
|
|
43
47
|
/**
|
|
44
48
|
* This method will result in the dashboard being reloaded.
|
|
49
|
+
*
|
|
50
|
+
* NOTE — this promise does NOT reject when the reload fails. A load error is caught, logged via
|
|
51
|
+
* `LogError` and surfaced on the {@link Error} output (see {@link runGuardedLoad}), and the
|
|
52
|
+
* promise then RESOLVES. That is deliberate: callers invoke `Refresh()` fire-and-forget (the
|
|
53
|
+
* container does), so rejecting here would produce an unhandled rejection rather than reaching
|
|
54
|
+
* anyone. If you need to know whether a refresh succeeded, subscribe to {@link Error} — do not
|
|
55
|
+
* rely on `await Refresh()` throwing.
|
|
45
56
|
*/
|
|
46
57
|
async Refresh() {
|
|
47
|
-
await this.loadData();
|
|
48
|
-
|
|
58
|
+
await this.runGuardedLoad(() => this.loadData());
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Runs a load phase (initial mount or Refresh) with the GUARANTEE that {@link NotifyLoadComplete}
|
|
62
|
+
* always fires — even if the work throws. The Explorer shell's loading screen blocks on that
|
|
63
|
+
* signal, so without this guard one dashboard's uncaught `initDashboard()`/`loadData()` error
|
|
64
|
+
* (e.g. an in-flight query rejecting while MJAPI restarts) would hang the entire shell forever on
|
|
65
|
+
* a direct/deep-link load. On error we log it and emit the {@link Error} output so the subclass or
|
|
66
|
+
* container can render its own error state; the loading screen still clears either way.
|
|
67
|
+
*/
|
|
68
|
+
async runGuardedLoad(work) {
|
|
69
|
+
try {
|
|
70
|
+
await work();
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
74
|
+
LogError(error);
|
|
75
|
+
this.Error.emit(error);
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
this.NotifyLoadComplete();
|
|
79
|
+
}
|
|
49
80
|
}
|
|
50
81
|
_visible = false;
|
|
51
82
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-dashboard.js","sourceRoot":"","sources":["../../src/lib/base-dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAqB,MAAM,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"base-dashboard.js","sourceRoot":"","sources":["../../src/lib/base-dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAqB,MAAM,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAgB,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;;AAOlE;;GAEG;AAEH,MAAM,OAAgB,aAAc,SAAQ,qBAAqB;IAC/D;;OAEG;IACH,IAAa,MAAM,CAAC,KAAsB;QACxC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACvB,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;OAEG;IACO,KAAK,GAAG,IAAI,YAAY,EAAS,CAAC;IAE5C;;OAEG;IACO,gBAAgB,GAAG,IAAI,YAAY,EAAO,CAAC;IAErD;;OAEG;IACO,WAAW,GAAG,IAAI,YAAY,EAAO,CAAC;IAEhD;;OAEG;IACO,gBAAgB,GAAG,IAAI,YAAY,EAAkD,CAAC;IAEtF,OAAO,GAA2B,IAAI,CAAC;IAEjD,KAAK,CAAC,QAAQ;QACZ,KAAK,CAAC,QAAQ,EAAE,CAAC;QACjB,6FAA6F;QAC7F,6EAA6E;QAC7E,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,IAAI,EAAE;YACnC,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,WAAW;QACT,KAAK,CAAC,WAAW,EAAE,CAAC;IACtB,CAAC;IAED;;;;;;;;;OASG;IACI,KAAK,CAAC,OAAO;QAClB,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACnD,CAAC;IAED;;;;;;;OAOG;IACK,KAAK,CAAC,cAAc,CAAC,IAAgC;QAC3D,IAAI,CAAC;YACH,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAClE,QAAQ,CAAC,KAAK,CAAC,CAAC;YAChB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC;IAEO,QAAQ,GAAY,KAAK,CAAC;IAClC;;OAEG;IACI,UAAU,CAAC,OAAgB;QAChC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IAC1B,CAAC;IAYD;;;;OAIG;IACH,KAAK,CAAC,oBAAoB,CAAC,IAAkB;QAC3C,OAAO,EAAE,CAAC;IACZ,CAAC;iOA3GmB,aAAa,yBAAb,aAAa;6DAAb,aAAa;;iFAAb,aAAa;cADlC,SAAS;;kBAKP,KAAK;;kBAWL,MAAM;;kBAKN,MAAM;;kBAKN,MAAM;;kBAKN,MAAM","sourcesContent":["import { Directive, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';\nimport { CompositeKey, LogError } from '@memberjunction/core';\nimport { MJDashboardEntityExtended, ResourceData } from '@memberjunction/core-entities';\nimport { BaseResourceComponent } from './base-resource-component';\n\nexport interface DashboardConfig {\n dashboard: MJDashboardEntityExtended;\n userState?: any;\n}\n\n/**\n * Make the base class a directive so we can use Angular functionality and sub-classes can be @Component \n */\n@Directive()\nexport abstract class BaseDashboard extends BaseResourceComponent implements OnInit, OnDestroy {\n /**\n * Set or change the dashboard configuration. Changing this property will NOT cause the dashboard to reload. Call Refresh() to do that.\n */\n @Input() set Config(value: DashboardConfig) {\n this._config = value;\n }\n\n get Config(): DashboardConfig | null {\n return this._config;\n }\n\n /**\n * Subclasses can emit anytime an error occurs. \n */\n @Output() Error = new EventEmitter<Error>();\n\n /**\n * Subclasses should emit this event anytime their internal state changes in a way that they'd like to persist.\n */\n @Output() UserStateChanged = new EventEmitter<any>();\n\n /**\n * Subclasses can emit this event anytime they want to communicate with the container to let it know that something has happened of significance.\n */\n @Output() Interaction = new EventEmitter<any>();\n\n /**\n * Subclasses can emit this event anytime they want to open a record within a particular entity. The container should handle this event and open the record.\n */\n @Output() OpenEntityRecord = new EventEmitter<{EntityName: string, RecordPKey: CompositeKey}>();\n\n protected _config: DashboardConfig | null = null;\n\n async ngOnInit() {\n super.ngOnInit();\n // initDashboard() runs before loadData() and can throw too, so BOTH are inside the guard — a\n // failure in either still reaches NotifyLoadComplete() (see runGuardedLoad).\n await this.runGuardedLoad(async () => {\n this.initDashboard();\n await this.loadData();\n });\n }\n\n ngOnDestroy(): void {\n super.ngOnDestroy();\n }\n\n /**\n * This method will result in the dashboard being reloaded.\n *\n * NOTE — this promise does NOT reject when the reload fails. A load error is caught, logged via\n * `LogError` and surfaced on the {@link Error} output (see {@link runGuardedLoad}), and the\n * promise then RESOLVES. That is deliberate: callers invoke `Refresh()` fire-and-forget (the\n * container does), so rejecting here would produce an unhandled rejection rather than reaching\n * anyone. If you need to know whether a refresh succeeded, subscribe to {@link Error} — do not\n * rely on `await Refresh()` throwing.\n */\n public async Refresh(): Promise<void> {\n await this.runGuardedLoad(() => this.loadData());\n }\n\n /**\n * Runs a load phase (initial mount or Refresh) with the GUARANTEE that {@link NotifyLoadComplete}\n * always fires — even if the work throws. The Explorer shell's loading screen blocks on that\n * signal, so without this guard one dashboard's uncaught `initDashboard()`/`loadData()` error\n * (e.g. an in-flight query rejecting while MJAPI restarts) would hang the entire shell forever on\n * a direct/deep-link load. On error we log it and emit the {@link Error} output so the subclass or\n * container can render its own error state; the loading screen still clears either way.\n */\n private async runGuardedLoad(work: () => void | Promise<void>): Promise<void> {\n try {\n await work();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n LogError(error);\n this.Error.emit(error);\n } finally {\n this.NotifyLoadComplete();\n }\n }\n\n private _visible: boolean = false;\n /**\n * This method can be used by a container to let the dashboard know that it is being opened/closed. Base class just sets a flag.\n */\n public SetVisible(visible: boolean): void {\n this._visible = visible;\n }\n\n /**\n * Subclasses can override this method to perform any initialization they need. This method only runs once when the dashboard is created.\n */\n protected abstract initDashboard(): void;\n\n /**\n * Subclasses should override this method to load their data. This method is called when the dashboard is created and when Refresh() is called.\n */\n protected abstract loadData(): void;\n\n /**\n * Sub-classes can override this to provide a custom icon class\n * @param data \n * @returns \n */\n async GetResourceIconClass(data: ResourceData): Promise<string> {\n return \"\";\n }\n} "]}
|
|
@@ -13,6 +13,14 @@ export declare abstract class BaseResourceComponent extends BaseNavigationCompon
|
|
|
13
13
|
private _lastDeliveredParamsKey;
|
|
14
14
|
protected destroy$: Subject<void>;
|
|
15
15
|
protected navigationService: NavigationService;
|
|
16
|
+
/**
|
|
17
|
+
* Optional: the tab container instantiates LIGHTWEIGHT throwaway instances
|
|
18
|
+
* of resource components (via runInInjectionContext with an environment
|
|
19
|
+
* injector) purely to call GetResourceDisplayName for background tab
|
|
20
|
+
* titles. ChangeDetectorRef only exists in node injectors, so a required
|
|
21
|
+
* inject() there throws NG0201 and silently kills every background title
|
|
22
|
+
* resolution — tabs keep raw "Entity - ID|guid" titles until opened.
|
|
23
|
+
*/
|
|
16
24
|
private changeDetectorRef;
|
|
17
25
|
/**
|
|
18
26
|
* Tab ID for query param notification scoping. Set by resource wrappers
|
|
@@ -20,15 +28,29 @@ export declare abstract class BaseResourceComponent extends BaseNavigationCompon
|
|
|
20
28
|
* If not set, falls back to Data.Configuration.tabId.
|
|
21
29
|
*/
|
|
22
30
|
ParentTabId: string | null;
|
|
31
|
+
/**
|
|
32
|
+
* Tab id this component was RE-HOMED to by a cache reattach. The
|
|
33
|
+
* component cache keys on driver+record+app — NOT tab id — so a cached
|
|
34
|
+
* component can be reattached to a different tab than it was born under.
|
|
35
|
+
* Without rebinding, its query-param subscription listens to the DEAD
|
|
36
|
+
* birth tab forever and every delivery to the live tab is lost (the
|
|
37
|
+
* "crumb lands on the dashboard root after promote/demote" bug).
|
|
38
|
+
*/
|
|
39
|
+
private reboundTabId;
|
|
40
|
+
/** Handle for the reactive param subscription so RebindTabId can replace it */
|
|
41
|
+
private reactiveParamSub;
|
|
23
42
|
get Data(): ResourceData;
|
|
24
43
|
set Data(value: ResourceData);
|
|
25
44
|
private _loadComplete;
|
|
26
45
|
get LoadComplete(): boolean;
|
|
27
46
|
/**
|
|
28
47
|
* Watchdog window: if a resource component hasn't called {@link NotifyLoadComplete} within this
|
|
29
|
-
* time, we log a warning naming the offending class
|
|
30
|
-
*
|
|
31
|
-
*
|
|
48
|
+
* time, we log a warning naming the offending class AND fail open — forcing NotifyLoadComplete
|
|
49
|
+
* so the app loading screen (which blocks on that signal) is released. Without the fail-open, a
|
|
50
|
+
* resource that never signals — because it errored, hung, or has its own `ngOnInit` that
|
|
51
|
+
* bypasses BaseDashboard's guarded lifecycle — would hang the whole Explorer forever. This is
|
|
52
|
+
* the safety net covering EVERY BaseResourceComponent subclass, not just BaseDashboard (whose
|
|
53
|
+
* own try/finally already guarantees the signal on the normal path).
|
|
32
54
|
*/
|
|
33
55
|
private static readonly LOAD_COMPLETE_WATCHDOG_MS;
|
|
34
56
|
private _loadCompleteWatchdog;
|
|
@@ -97,6 +119,14 @@ export declare abstract class BaseResourceComponent extends BaseNavigationCompon
|
|
|
97
119
|
* wrappers for child dashboards), then falls back to Data.Configuration.tabId.
|
|
98
120
|
*/
|
|
99
121
|
getTabId(): string;
|
|
122
|
+
/**
|
|
123
|
+
* Re-home this component to a different tab (cache reattach across tab
|
|
124
|
+
* ids). Replaces the reactive param subscription with one bound to the
|
|
125
|
+
* new tab and resets the duplicate-delivery key so the new tab's
|
|
126
|
+
* CURRENT params apply as a fresh mount would — the replay-on-subscribe
|
|
127
|
+
* delivers them immediately.
|
|
128
|
+
*/
|
|
129
|
+
RebindTabId(tabId: string): void;
|
|
100
130
|
private getQueryParamUpdateGuard;
|
|
101
131
|
protected NotifyLoadComplete(): void;
|
|
102
132
|
protected NotifyLoadStarted(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-resource-component.d.ts","sourceRoot":"","sources":["../../src/lib/base-resource-component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,MAAM,EAAE,SAAS,EAAiB,MAAM,eAAe,CAAC;AAC/F,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"base-resource-component.d.ts","sourceRoot":"","sources":["../../src/lib/base-resource-component.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,14 @@ export class BaseResourceComponent extends BaseNavigationComponent {
|
|
|
13
13
|
_lastDeliveredParamsKey = null;
|
|
14
14
|
destroy$ = new Subject();
|
|
15
15
|
navigationService = inject(NavigationService);
|
|
16
|
+
/**
|
|
17
|
+
* Optional: the tab container instantiates LIGHTWEIGHT throwaway instances
|
|
18
|
+
* of resource components (via runInInjectionContext with an environment
|
|
19
|
+
* injector) purely to call GetResourceDisplayName for background tab
|
|
20
|
+
* titles. ChangeDetectorRef only exists in node injectors, so a required
|
|
21
|
+
* inject() there throws NG0201 and silently kills every background title
|
|
22
|
+
* resolution — tabs keep raw "Entity - ID|guid" titles until opened.
|
|
23
|
+
*/
|
|
16
24
|
changeDetectorRef = inject(ChangeDetectorRef, { optional: true });
|
|
17
25
|
/**
|
|
18
26
|
* Tab ID for query param notification scoping. Set by resource wrappers
|
|
@@ -20,6 +28,17 @@ export class BaseResourceComponent extends BaseNavigationComponent {
|
|
|
20
28
|
* If not set, falls back to Data.Configuration.tabId.
|
|
21
29
|
*/
|
|
22
30
|
ParentTabId = null;
|
|
31
|
+
/**
|
|
32
|
+
* Tab id this component was RE-HOMED to by a cache reattach. The
|
|
33
|
+
* component cache keys on driver+record+app — NOT tab id — so a cached
|
|
34
|
+
* component can be reattached to a different tab than it was born under.
|
|
35
|
+
* Without rebinding, its query-param subscription listens to the DEAD
|
|
36
|
+
* birth tab forever and every delivery to the live tab is lost (the
|
|
37
|
+
* "crumb lands on the dashboard root after promote/demote" bug).
|
|
38
|
+
*/
|
|
39
|
+
reboundTabId = null;
|
|
40
|
+
/** Handle for the reactive param subscription so RebindTabId can replace it */
|
|
41
|
+
reactiveParamSub = null;
|
|
23
42
|
get Data() {
|
|
24
43
|
return this._data;
|
|
25
44
|
}
|
|
@@ -32,9 +51,12 @@ export class BaseResourceComponent extends BaseNavigationComponent {
|
|
|
32
51
|
}
|
|
33
52
|
/**
|
|
34
53
|
* Watchdog window: if a resource component hasn't called {@link NotifyLoadComplete} within this
|
|
35
|
-
* time, we log a warning naming the offending class
|
|
36
|
-
*
|
|
37
|
-
*
|
|
54
|
+
* time, we log a warning naming the offending class AND fail open — forcing NotifyLoadComplete
|
|
55
|
+
* so the app loading screen (which blocks on that signal) is released. Without the fail-open, a
|
|
56
|
+
* resource that never signals — because it errored, hung, or has its own `ngOnInit` that
|
|
57
|
+
* bypasses BaseDashboard's guarded lifecycle — would hang the whole Explorer forever. This is
|
|
58
|
+
* the safety net covering EVERY BaseResourceComponent subclass, not just BaseDashboard (whose
|
|
59
|
+
* own try/finally already guarantees the signal on the normal path).
|
|
38
60
|
*/
|
|
39
61
|
static LOAD_COMPLETE_WATCHDOG_MS = 15_000;
|
|
40
62
|
_loadCompleteWatchdog = null;
|
|
@@ -89,9 +111,17 @@ export class BaseResourceComponent extends BaseNavigationComponent {
|
|
|
89
111
|
if (!this._loadComplete) {
|
|
90
112
|
// eslint-disable-next-line no-console
|
|
91
113
|
console.warn(`[LoadComplete WATCHDOG] ${this.constructor.name} has NOT called NotifyLoadComplete() ` +
|
|
92
|
-
`within ${BaseResourceComponent.LOAD_COMPLETE_WATCHDOG_MS / 1000}s
|
|
93
|
-
`
|
|
114
|
+
`within ${BaseResourceComponent.LOAD_COMPLETE_WATCHDOG_MS / 1000}s — FAILING OPEN: forcing ` +
|
|
115
|
+
`load-complete to release the app loading screen. This resource either errored/hung without ` +
|
|
116
|
+
`signalling, or is simply still loading (a slow-but-healthy load re-signals harmlessly when ` +
|
|
117
|
+
`it finishes). If you are chasing a "stuck loading" report, start here. ` +
|
|
94
118
|
`(tabId=${this.getTabId() || 'n/a'})`);
|
|
119
|
+
// Fail-open: the shell's loading screen blocks on NotifyLoadComplete. A subclass that
|
|
120
|
+
// never calls it — one whose own ngOnInit bypasses BaseDashboard's guarded lifecycle,
|
|
121
|
+
// or whose load genuinely hangs — would otherwise brick the whole Explorer. Force the
|
|
122
|
+
// signal so no single resource can take down the shell. (Idempotent-safe: NotifyLoad
|
|
123
|
+
// Complete is already called repeatedly on the normal path, e.g. every Refresh.)
|
|
124
|
+
this.NotifyLoadComplete();
|
|
95
125
|
}
|
|
96
126
|
}, BaseResourceComponent.LOAD_COMPLETE_WATCHDOG_MS);
|
|
97
127
|
}
|
|
@@ -145,7 +175,7 @@ export class BaseResourceComponent extends BaseNavigationComponent {
|
|
|
145
175
|
const myTabId = this.getTabId();
|
|
146
176
|
return !myTabId || event.TabId === myTabId;
|
|
147
177
|
}), takeUntil(this.destroy$))
|
|
148
|
-
.subscribe(event => this.deliverQueryParams(event.Params));
|
|
178
|
+
.subscribe(event => this.deliverQueryParams(event.Params, event.Force === true));
|
|
149
179
|
}
|
|
150
180
|
/**
|
|
151
181
|
* Internal: reactively deliver this tab's query params from the workspace
|
|
@@ -160,7 +190,8 @@ export class BaseResourceComponent extends BaseNavigationComponent {
|
|
|
160
190
|
if (!tabId) {
|
|
161
191
|
return; // No tab scope (e.g. embedded usage) — nothing to observe.
|
|
162
192
|
}
|
|
163
|
-
this.
|
|
193
|
+
this.reactiveParamSub?.unsubscribe();
|
|
194
|
+
this.reactiveParamSub = this.navigationService.ObserveTabQueryParams(tabId)
|
|
164
195
|
.pipe(takeUntil(this.destroy$))
|
|
165
196
|
.subscribe(params => this.deliverQueryParams(params));
|
|
166
197
|
}
|
|
@@ -169,16 +200,23 @@ export class BaseResourceComponent extends BaseNavigationComponent {
|
|
|
169
200
|
* identical deliveries (the two paths overlap on back/forward) and labels the first
|
|
170
201
|
* meaningful delivery as a 'deeplink', subsequent ones as 'popstate'.
|
|
171
202
|
*/
|
|
172
|
-
deliverQueryParams(params) {
|
|
203
|
+
deliverQueryParams(params, force = false) {
|
|
173
204
|
const key = this.queryParamsKey(params);
|
|
174
|
-
|
|
205
|
+
// The duplicate guard assumes delivered params == applied state, but a
|
|
206
|
+
// component's state can DRIFT after a delivery (internal navigation
|
|
207
|
+
// whose write-back was suppressed or lost). Explicit restores (origin
|
|
208
|
+
// crumb) pass force to reach OnQueryParamsChanged regardless.
|
|
209
|
+
if (key === this._lastDeliveredParamsKey && !force) {
|
|
175
210
|
return; // Already delivered these exact params.
|
|
176
211
|
}
|
|
177
212
|
const isInitial = this._lastDeliveredParamsKey === null;
|
|
178
213
|
// Don't fire an initial no-op: a component entered without deep-link params has
|
|
179
214
|
// nothing to apply. Leave _lastDeliveredParamsKey null so the first real params
|
|
180
215
|
// (whenever they arrive) are still treated as the deep-link entry.
|
|
181
|
-
|
|
216
|
+
// FORCED empties bypass this too: an explicit reset (SwitchToAppHome
|
|
217
|
+
// returning a drifted dashboard to its landing) must reach
|
|
218
|
+
// OnQueryParamsChanged even when nothing was ever delivered.
|
|
219
|
+
if (isInitial && Object.keys(params).length === 0 && !force) {
|
|
182
220
|
return;
|
|
183
221
|
}
|
|
184
222
|
this._lastDeliveredParamsKey = key;
|
|
@@ -210,7 +248,22 @@ export class BaseResourceComponent extends BaseNavigationComponent {
|
|
|
210
248
|
* wrappers for child dashboards), then falls back to Data.Configuration.tabId.
|
|
211
249
|
*/
|
|
212
250
|
getTabId() {
|
|
213
|
-
return this.ParentTabId || this.Data?.Configuration?.['tabId'] || '';
|
|
251
|
+
return this.ParentTabId || this.reboundTabId || this.Data?.Configuration?.['tabId'] || '';
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Re-home this component to a different tab (cache reattach across tab
|
|
255
|
+
* ids). Replaces the reactive param subscription with one bound to the
|
|
256
|
+
* new tab and resets the duplicate-delivery key so the new tab's
|
|
257
|
+
* CURRENT params apply as a fresh mount would — the replay-on-subscribe
|
|
258
|
+
* delivers them immediately.
|
|
259
|
+
*/
|
|
260
|
+
RebindTabId(tabId) {
|
|
261
|
+
if (this.getTabId() === tabId) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
this.reboundTabId = tabId;
|
|
265
|
+
this._lastDeliveredParamsKey = null;
|
|
266
|
+
this.setupInitialParamDelivery();
|
|
214
267
|
}
|
|
215
268
|
getQueryParamUpdateGuard() {
|
|
216
269
|
const config = this.Data?.Configuration || {};
|
|
@@ -261,6 +314,7 @@ export class BaseResourceComponent extends BaseNavigationComponent {
|
|
|
261
314
|
* any LATER async state change that must reach the DOM outside those signals.
|
|
262
315
|
*/
|
|
263
316
|
RefreshView() {
|
|
317
|
+
// Null on lightweight non-view instances (background title resolution)
|
|
264
318
|
this.changeDetectorRef?.markForCheck();
|
|
265
319
|
}
|
|
266
320
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-resource-component.js","sourceRoot":"","sources":["../../src/lib/base-resource-component.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeDetectorRef, Directive, OnInit, OnDestroy, Input, inject } from \"@angular/core\";\nimport { Subject } from \"rxjs\";\nimport { filter, takeUntil } from \"rxjs/operators\";\nimport { BaseEntity } from \"@memberjunction/core\";\nimport { BaseNavigationComponent } from \"./base-navigation-component\";\nimport { ResourceData } from \"@memberjunction/core-entities\";\nimport { NavigationService, TabQueryParamUpdateGuard } from \"./navigation.service\";\n\n@Directive()\nexport abstract class BaseResourceComponent extends BaseNavigationComponent implements OnInit, OnDestroy {\n private _data: ResourceData = new ResourceData();\n private _suppressQueryParamSync = false;\n /** Stable key of the most recently delivered query params, used to de-duplicate\n * delivery across the reactive (initial/deep-link) and popstate paths. */\n private _lastDeliveredParamsKey: string | null = null;\n protected destroy$ = new Subject<void>();\n protected navigationService = inject(NavigationService);\n private changeDetectorRef = inject(ChangeDetectorRef, { optional: true });\n\n /**\n * Tab ID for query param notification scoping. Set by resource wrappers\n * that render child dashboards, so the child knows which tab it belongs to.\n * If not set, falls back to Data.Configuration.tabId.\n */\n @Input() ParentTabId: string | null = null;\n\n public get Data(): ResourceData {\n return this._data;\n }\n public set Data(value: ResourceData) {\n this._data = value;\n }\n\n private _loadComplete: boolean = false;\n public get LoadComplete(): boolean {\n return this._loadComplete;\n }\n\n /**\n * Watchdog window: if a resource component hasn't called {@link NotifyLoadComplete} within this\n * time, we log a warning naming the offending class. The app loading screen waits on\n * NotifyLoadComplete, so a resource that never calls it hangs the screen forever — this makes the\n * culprit obvious in the console instead of leaving a mystery \"stuck loading\" state.\n */\n private static readonly LOAD_COMPLETE_WATCHDOG_MS = 15_000;\n private _loadCompleteWatchdog: ReturnType<typeof setTimeout> | null = null;\n\n private _loadStarted: boolean = false;\n public get LoadStarted(): boolean {\n return this._loadStarted;\n }\n\n\n private _loadCompleteEvent: any = null;\n public get LoadCompleteEvent(): any {\n return this._loadCompleteEvent\n }\n public set LoadCompleteEvent(value: any) {\n this._loadCompleteEvent = value;\n }\n\n private _loadStartedEvent: any = null;\n public get LoadStartedEvent(): any {\n return this._loadStartedEvent\n }\n public set LoadStartedEvent(value: any) {\n this._loadStartedEvent = value;\n }\n\n private _resourceRecordSavedEvent: any = null;\n public get ResourceRecordSavedEvent(): any {\n return this._resourceRecordSavedEvent\n }\n public set ResourceRecordSavedEvent(value: any) {\n this._resourceRecordSavedEvent = value;\n }\n\n private _resourceCloseRequestedEvent: (() => void) | null = null;\n public get ResourceCloseRequestedEvent(): (() => void) | null {\n return this._resourceCloseRequestedEvent;\n }\n public set ResourceCloseRequestedEvent(value: (() => void) | null) {\n this._resourceCloseRequestedEvent = value;\n }\n\n private _displayNameChangedEvent: ((newName: string) => void) | null = null;\n public get DisplayNameChangedEvent(): ((newName: string) => void) | null {\n return this._displayNameChangedEvent;\n }\n public set DisplayNameChangedEvent(value: ((newName: string) => void) | null) {\n this._displayNameChangedEvent = value;\n }\n\n ngOnInit(): void {\n // Order matters: set up the popstate subscription first (a plain Subject — emits\n // nothing on subscribe), then the reactive initial-delivery subscription (backed by\n // a BehaviorSubject — emits the current params synchronously). This lets the initial\n // emission claim the 'deeplink' source before any later popstate event.\n this.setupQueryParamSubscription();\n this.setupInitialParamDelivery();\n\n // Start the NotifyLoadComplete watchdog (no-op once it fires or load completes).\n this._loadCompleteWatchdog = setTimeout(() => {\n if (!this._loadComplete) {\n // eslint-disable-next-line no-console\n console.warn(\n `[LoadComplete WATCHDOG] ${this.constructor.name} has NOT called NotifyLoadComplete() ` +\n `within ${BaseResourceComponent.LOAD_COMPLETE_WATCHDOG_MS / 1000}s. The app loading screen ` +\n `waits on this signal — if the screen is stuck, this resource is the likely cause. ` +\n `(tabId=${this.getTabId() || 'n/a'})`\n );\n }\n }, BaseResourceComponent.LOAD_COMPLETE_WATCHDOG_MS);\n }\n\n ngOnDestroy(): void {\n if (this._loadCompleteWatchdog) {\n clearTimeout(this._loadCompleteWatchdog);\n this._loadCompleteWatchdog = null;\n }\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * Called by the framework when query params change from an external source\n * (browser back/forward, deep link navigation).\n * Override in subclasses to react to query param changes.\n * @param params The new query params from the URL\n * @param source 'popstate' for back/forward, 'deeplink' for external URL entry\n */\n protected OnQueryParamsChanged(params: Record<string, string>, source: 'popstate' | 'deeplink'): void {\n // Default no-op — override in subclasses\n }\n\n /**\n * Push query param changes to the URL. Creates a browser history entry.\n * Safe to call during OnQueryParamsChanged — auto-suppressed to prevent loops.\n */\n protected UpdateQueryParams(params: Record<string, string | null>): void {\n if (this._suppressQueryParamSync) return;\n const tabId = this.getTabId();\n if (!tabId) {\n this.navigationService.UpdateActiveTabQueryParams(params);\n return;\n }\n\n this.navigationService.UpdateTabQueryParams(tabId, params, this.getQueryParamUpdateGuard());\n }\n\n /**\n * Read current query params from tab configuration.\n * Use in initDashboard() / ngOnInit() to get initial URL state.\n */\n protected GetQueryParams(): Record<string, string> {\n return (this.Data?.Configuration?.['queryParams'] as Record<string, string>) || {};\n }\n\n /**\n * Internal: subscribe to NavigationService query param notifications.\n * Filters to only this component's tab to prevent cross-tab leakage.\n * This is the explicit back/forward (popstate) path.\n */\n private setupQueryParamSubscription(): void {\n this.navigationService.QueryParamChanged$\n .pipe(\n filter(event => {\n const myTabId = this.getTabId();\n return !myTabId || event.TabId === myTabId;\n }),\n takeUntil(this.destroy$)\n )\n .subscribe(event => this.deliverQueryParams(event.Params));\n }\n\n /**\n * Internal: reactively deliver this tab's query params from the workspace\n * BehaviorSubject. Because the source replays the current value on subscribe and\n * emits future changes, this delivers initial deep-link params even when the\n * component mounts (e.g. from workspace restoration) before the URL params have\n * been merged into the tab configuration — closing the cold-load race that the\n * popstate-only path could not.\n */\n private setupInitialParamDelivery(): void {\n const tabId = this.getTabId();\n if (!tabId) {\n return; // No tab scope (e.g. embedded usage) — nothing to observe.\n }\n this.navigationService.ObserveTabQueryParams(tabId)\n .pipe(takeUntil(this.destroy$))\n .subscribe(params => this.deliverQueryParams(params));\n }\n\n /**\n * Internal: funnel point for both the reactive and popstate paths. De-duplicates\n * identical deliveries (the two paths overlap on back/forward) and labels the first\n * meaningful delivery as a 'deeplink', subsequent ones as 'popstate'.\n */\n private deliverQueryParams(params: Record<string, string>): void {\n const key = this.queryParamsKey(params);\n if (key === this._lastDeliveredParamsKey) {\n return; // Already delivered these exact params.\n }\n const isInitial = this._lastDeliveredParamsKey === null;\n // Don't fire an initial no-op: a component entered without deep-link params has\n // nothing to apply. Leave _lastDeliveredParamsKey null so the first real params\n // (whenever they arrive) are still treated as the deep-link entry.\n if (isInitial && Object.keys(params).length === 0) {\n return;\n }\n this._lastDeliveredParamsKey = key;\n const source: 'popstate' | 'deeplink' = isInitial ? 'deeplink' : 'popstate';\n // try/finally ensures the suppression flag is always cleared, and the view is always\n // refreshed, even if OnQueryParamsChanged throws.\n this._suppressQueryParamSync = true;\n try {\n this.OnQueryParamsChanged(params, source);\n } finally {\n this._suppressQueryParamSync = false;\n // Query params arrive via RxJS (workspace stream / popstate), not via a template event —\n // state a subclass changed in OnQueryParamsChanged needs an explicit re-render request\n // for dynamically-attached views (see RefreshView). Inside `finally` so a subclass\n // override that throws still renders whatever partial state it applied before throwing.\n this.RefreshView();\n }\n }\n\n /** Stable, order-independent string key for a query param record. */\n private queryParamsKey(params: Record<string, string>): string {\n return Object.keys(params)\n .sort()\n .map(k => `${k}=${params[k]}`)\n .join('&');\n }\n\n /**\n * Get this component's tab ID. Checks ParentTabId input first (set by resource\n * wrappers for child dashboards), then falls back to Data.Configuration.tabId.\n */\n public getTabId(): string {\n return this.ParentTabId || this.Data?.Configuration?.['tabId'] as string || '';\n }\n\n private getQueryParamUpdateGuard(): TabQueryParamUpdateGuard {\n const config = this.Data?.Configuration || {};\n return {\n resourceType: config['resourceType'] as string | undefined,\n driverClass: (config['resourceTypeDriverClass'] || config['driverClass']) as string | undefined,\n recordId: (this.Data?.ResourceRecordID || config['recordId']) as string | undefined,\n navItemName: config['navItemName'] as string | undefined,\n entity: (config['Entity'] || config['entity']) as string | undefined\n };\n }\n\n protected NotifyLoadComplete() {\n this._loadComplete = true;\n if (this._loadCompleteWatchdog) {\n clearTimeout(this._loadCompleteWatchdog);\n this._loadCompleteWatchdog = null;\n }\n if (this._loadCompleteEvent) {\n this._loadCompleteEvent();\n }\n // Load completion almost always follows async work (RunView/fetch continuations) that\n // Angular's tick will NOT pick up on its own for dynamically-attached views — re-render\n // must be requested explicitly. See RefreshView() for the full explanation.\n this.RefreshView();\n }\n\n protected NotifyLoadStarted() {\n this._loadStarted = true;\n if (this._loadStartedEvent) {\n this._loadStartedEvent();\n }\n this.RefreshView();\n }\n\n /**\n * Marks this component's view (and its ancestors) as needing change detection and schedules\n * a tick — the reliable way for a resource component to re-render after updating state from\n * async work (RunView/RunQuery continuations, timers, websocket pushes, RxJS subscriptions).\n *\n * WHY THIS EXISTS: the Explorer shell hosts resource components dynamically — `createComponent()`\n * + `ApplicationRef.attachView()` — because tab content mounts into Golden Layout / cached DOM\n * containers, not into an Angular template. Since Angular 18's change-detection scheduler\n * rework (Explorer is on Angular 21), `ApplicationRef.tick()` only refreshes attached views\n * that are FLAGGED dirty; a root-level attached view whose component mutates plain fields from\n * an async continuation is never flagged, so the view silently never re-renders — even though\n * the component uses default (non-OnPush) change detection (issue #3106).\n *\n * The framework calls this automatically from NotifyLoadStarted() / NotifyLoadComplete(), so\n * the standard load lifecycle re-renders without any subclass action. Call it yourself after\n * any LATER async state change that must reach the DOM outside those signals.\n */\n protected RefreshView(): void {\n this.changeDetectorRef?.markForCheck();\n }\n\n\n\n /**\n * Call this to notify the tab system that the resource's display name has changed.\n * The tab container will update the tab title and browser title accordingly.\n */\n protected NotifyDisplayNameChanged(newName: string): void {\n if (this._displayNameChangedEvent) {\n this._displayNameChangedEvent(newName);\n }\n }\n\n protected ResourceRecordSaved(resourceRecordEntity: BaseEntity) {\n // Use ToURLSegment, NOT ToString. ResourceRecordID is consumed by\n // EntityRecordResource.GetPrimaryKey → CompositeKey.LoadFromURLSegment, which\n // expects URL-segment format (e.g. \"ID|<uuid>\"). ToString produces \"ID=<uuid>\",\n // which LoadFromURLSegment treats as a raw value and double-prefixes —\n // breaking any code path that re-reads this field after save.\n this.Data.ResourceRecordID = resourceRecordEntity.PrimaryKey.ToURLSegment();\n if (this._resourceRecordSavedEvent) {\n this._resourceRecordSavedEvent(resourceRecordEntity);\n }\n }\n\n /**\n * Ask the host shell to close/dismiss this resource (typically: close the tab).\n * Called by subclasses that hosted a form that emitted a 'dismiss' navigation\n * event — most often, a brand-new record where the user clicked Discard. The\n * record was never saved, the form is empty, and leaving it open serves no\n * purpose. The tab-container listens for this and closes the workspace tab.\n */\n protected NotifyCloseRequested(): void {\n if (this._resourceCloseRequestedEvent) {\n this._resourceCloseRequestedEvent();\n }\n }\n\n abstract GetResourceDisplayName(data: ResourceData): Promise<string>\n\n abstract GetResourceIconClass(data: ResourceData): Promise<string>\n}\n"]}
|
|
1
|
+
{"version":3,"file":"base-resource-component.js","sourceRoot":"","sources":["../../src/lib/base-resource-component.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeDetectorRef, Directive, OnInit, OnDestroy, Input, inject } from \"@angular/core\";\nimport { Subject, Subscription } from \"rxjs\";\nimport { filter, takeUntil } from \"rxjs/operators\";\nimport { BaseEntity } from \"@memberjunction/core\";\nimport { BaseNavigationComponent } from \"./base-navigation-component\";\nimport { ResourceData } from \"@memberjunction/core-entities\";\nimport { NavigationService, TabQueryParamUpdateGuard } from \"./navigation.service\";\n\n@Directive()\nexport abstract class BaseResourceComponent extends BaseNavigationComponent implements OnInit, OnDestroy {\n private _data: ResourceData = new ResourceData();\n private _suppressQueryParamSync = false;\n /** Stable key of the most recently delivered query params, used to de-duplicate\n * delivery across the reactive (initial/deep-link) and popstate paths. */\n private _lastDeliveredParamsKey: string | null = null;\n protected destroy$ = new Subject<void>();\n protected navigationService = inject(NavigationService);\n /**\n * Optional: the tab container instantiates LIGHTWEIGHT throwaway instances\n * of resource components (via runInInjectionContext with an environment\n * injector) purely to call GetResourceDisplayName for background tab\n * titles. ChangeDetectorRef only exists in node injectors, so a required\n * inject() there throws NG0201 and silently kills every background title\n * resolution — tabs keep raw \"Entity - ID|guid\" titles until opened.\n */\n private changeDetectorRef = inject(ChangeDetectorRef, { optional: true });\n\n /**\n * Tab ID for query param notification scoping. Set by resource wrappers\n * that render child dashboards, so the child knows which tab it belongs to.\n * If not set, falls back to Data.Configuration.tabId.\n */\n @Input() ParentTabId: string | null = null;\n\n /**\n * Tab id this component was RE-HOMED to by a cache reattach. The\n * component cache keys on driver+record+app — NOT tab id — so a cached\n * component can be reattached to a different tab than it was born under.\n * Without rebinding, its query-param subscription listens to the DEAD\n * birth tab forever and every delivery to the live tab is lost (the\n * \"crumb lands on the dashboard root after promote/demote\" bug).\n */\n private reboundTabId: string | null = null;\n\n /** Handle for the reactive param subscription so RebindTabId can replace it */\n private reactiveParamSub: Subscription | null = null;\n\n public get Data(): ResourceData {\n return this._data;\n }\n public set Data(value: ResourceData) {\n this._data = value;\n }\n\n private _loadComplete: boolean = false;\n public get LoadComplete(): boolean {\n return this._loadComplete;\n }\n\n /**\n * Watchdog window: if a resource component hasn't called {@link NotifyLoadComplete} within this\n * time, we log a warning naming the offending class AND fail open — forcing NotifyLoadComplete\n * so the app loading screen (which blocks on that signal) is released. Without the fail-open, a\n * resource that never signals — because it errored, hung, or has its own `ngOnInit` that\n * bypasses BaseDashboard's guarded lifecycle — would hang the whole Explorer forever. This is\n * the safety net covering EVERY BaseResourceComponent subclass, not just BaseDashboard (whose\n * own try/finally already guarantees the signal on the normal path).\n */\n private static readonly LOAD_COMPLETE_WATCHDOG_MS = 15_000;\n private _loadCompleteWatchdog: ReturnType<typeof setTimeout> | null = null;\n\n private _loadStarted: boolean = false;\n public get LoadStarted(): boolean {\n return this._loadStarted;\n }\n\n\n private _loadCompleteEvent: any = null;\n public get LoadCompleteEvent(): any {\n return this._loadCompleteEvent\n }\n public set LoadCompleteEvent(value: any) {\n this._loadCompleteEvent = value;\n }\n\n private _loadStartedEvent: any = null;\n public get LoadStartedEvent(): any {\n return this._loadStartedEvent\n }\n public set LoadStartedEvent(value: any) {\n this._loadStartedEvent = value;\n }\n\n private _resourceRecordSavedEvent: any = null;\n public get ResourceRecordSavedEvent(): any {\n return this._resourceRecordSavedEvent\n }\n public set ResourceRecordSavedEvent(value: any) {\n this._resourceRecordSavedEvent = value;\n }\n\n private _resourceCloseRequestedEvent: (() => void) | null = null;\n public get ResourceCloseRequestedEvent(): (() => void) | null {\n return this._resourceCloseRequestedEvent;\n }\n public set ResourceCloseRequestedEvent(value: (() => void) | null) {\n this._resourceCloseRequestedEvent = value;\n }\n\n private _displayNameChangedEvent: ((newName: string) => void) | null = null;\n public get DisplayNameChangedEvent(): ((newName: string) => void) | null {\n return this._displayNameChangedEvent;\n }\n public set DisplayNameChangedEvent(value: ((newName: string) => void) | null) {\n this._displayNameChangedEvent = value;\n }\n\n ngOnInit(): void {\n // Order matters: set up the popstate subscription first (a plain Subject — emits\n // nothing on subscribe), then the reactive initial-delivery subscription (backed by\n // a BehaviorSubject — emits the current params synchronously). This lets the initial\n // emission claim the 'deeplink' source before any later popstate event.\n this.setupQueryParamSubscription();\n this.setupInitialParamDelivery();\n\n // Start the NotifyLoadComplete watchdog (no-op once it fires or load completes).\n this._loadCompleteWatchdog = setTimeout(() => {\n if (!this._loadComplete) {\n // eslint-disable-next-line no-console\n console.warn(\n `[LoadComplete WATCHDOG] ${this.constructor.name} has NOT called NotifyLoadComplete() ` +\n `within ${BaseResourceComponent.LOAD_COMPLETE_WATCHDOG_MS / 1000}s — FAILING OPEN: forcing ` +\n `load-complete to release the app loading screen. This resource either errored/hung without ` +\n `signalling, or is simply still loading (a slow-but-healthy load re-signals harmlessly when ` +\n `it finishes). If you are chasing a \"stuck loading\" report, start here. ` +\n `(tabId=${this.getTabId() || 'n/a'})`\n );\n // Fail-open: the shell's loading screen blocks on NotifyLoadComplete. A subclass that\n // never calls it — one whose own ngOnInit bypasses BaseDashboard's guarded lifecycle,\n // or whose load genuinely hangs — would otherwise brick the whole Explorer. Force the\n // signal so no single resource can take down the shell. (Idempotent-safe: NotifyLoad\n // Complete is already called repeatedly on the normal path, e.g. every Refresh.)\n this.NotifyLoadComplete();\n }\n }, BaseResourceComponent.LOAD_COMPLETE_WATCHDOG_MS);\n }\n\n ngOnDestroy(): void {\n if (this._loadCompleteWatchdog) {\n clearTimeout(this._loadCompleteWatchdog);\n this._loadCompleteWatchdog = null;\n }\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * Called by the framework when query params change from an external source\n * (browser back/forward, deep link navigation).\n * Override in subclasses to react to query param changes.\n * @param params The new query params from the URL\n * @param source 'popstate' for back/forward, 'deeplink' for external URL entry\n */\n protected OnQueryParamsChanged(params: Record<string, string>, source: 'popstate' | 'deeplink'): void {\n // Default no-op — override in subclasses\n }\n\n /**\n * Push query param changes to the URL. Creates a browser history entry.\n * Safe to call during OnQueryParamsChanged — auto-suppressed to prevent loops.\n */\n protected UpdateQueryParams(params: Record<string, string | null>): void {\n if (this._suppressQueryParamSync) return;\n const tabId = this.getTabId();\n if (!tabId) {\n this.navigationService.UpdateActiveTabQueryParams(params);\n return;\n }\n\n this.navigationService.UpdateTabQueryParams(tabId, params, this.getQueryParamUpdateGuard());\n }\n\n /**\n * Read current query params from tab configuration.\n * Use in initDashboard() / ngOnInit() to get initial URL state.\n */\n protected GetQueryParams(): Record<string, string> {\n return (this.Data?.Configuration?.['queryParams'] as Record<string, string>) || {};\n }\n\n /**\n * Internal: subscribe to NavigationService query param notifications.\n * Filters to only this component's tab to prevent cross-tab leakage.\n * This is the explicit back/forward (popstate) path.\n */\n private setupQueryParamSubscription(): void {\n this.navigationService.QueryParamChanged$\n .pipe(\n filter(event => {\n const myTabId = this.getTabId();\n return !myTabId || event.TabId === myTabId;\n }),\n takeUntil(this.destroy$)\n )\n .subscribe(event => this.deliverQueryParams(event.Params, event.Force === true));\n }\n\n /**\n * Internal: reactively deliver this tab's query params from the workspace\n * BehaviorSubject. Because the source replays the current value on subscribe and\n * emits future changes, this delivers initial deep-link params even when the\n * component mounts (e.g. from workspace restoration) before the URL params have\n * been merged into the tab configuration — closing the cold-load race that the\n * popstate-only path could not.\n */\n private setupInitialParamDelivery(): void {\n const tabId = this.getTabId();\n if (!tabId) {\n return; // No tab scope (e.g. embedded usage) — nothing to observe.\n }\n this.reactiveParamSub?.unsubscribe();\n this.reactiveParamSub = this.navigationService.ObserveTabQueryParams(tabId)\n .pipe(takeUntil(this.destroy$))\n .subscribe(params => this.deliverQueryParams(params));\n }\n\n /**\n * Internal: funnel point for both the reactive and popstate paths. De-duplicates\n * identical deliveries (the two paths overlap on back/forward) and labels the first\n * meaningful delivery as a 'deeplink', subsequent ones as 'popstate'.\n */\n private deliverQueryParams(params: Record<string, string>, force = false): void {\n const key = this.queryParamsKey(params);\n // The duplicate guard assumes delivered params == applied state, but a\n // component's state can DRIFT after a delivery (internal navigation\n // whose write-back was suppressed or lost). Explicit restores (origin\n // crumb) pass force to reach OnQueryParamsChanged regardless.\n if (key === this._lastDeliveredParamsKey && !force) {\n return; // Already delivered these exact params.\n }\n const isInitial = this._lastDeliveredParamsKey === null;\n // Don't fire an initial no-op: a component entered without deep-link params has\n // nothing to apply. Leave _lastDeliveredParamsKey null so the first real params\n // (whenever they arrive) are still treated as the deep-link entry.\n // FORCED empties bypass this too: an explicit reset (SwitchToAppHome\n // returning a drifted dashboard to its landing) must reach\n // OnQueryParamsChanged even when nothing was ever delivered.\n if (isInitial && Object.keys(params).length === 0 && !force) {\n return;\n }\n this._lastDeliveredParamsKey = key;\n const source: 'popstate' | 'deeplink' = isInitial ? 'deeplink' : 'popstate';\n // try/finally ensures the suppression flag is always cleared, and the view is always\n // refreshed, even if OnQueryParamsChanged throws.\n this._suppressQueryParamSync = true;\n try {\n this.OnQueryParamsChanged(params, source);\n } finally {\n this._suppressQueryParamSync = false;\n // Query params arrive via RxJS (workspace stream / popstate), not via a template event —\n // state a subclass changed in OnQueryParamsChanged needs an explicit re-render request\n // for dynamically-attached views (see RefreshView). Inside `finally` so a subclass\n // override that throws still renders whatever partial state it applied before throwing.\n this.RefreshView();\n }\n }\n\n /** Stable, order-independent string key for a query param record. */\n private queryParamsKey(params: Record<string, string>): string {\n return Object.keys(params)\n .sort()\n .map(k => `${k}=${params[k]}`)\n .join('&');\n }\n\n /**\n * Get this component's tab ID. Checks ParentTabId input first (set by resource\n * wrappers for child dashboards), then falls back to Data.Configuration.tabId.\n */\n public getTabId(): string {\n return this.ParentTabId || this.reboundTabId || this.Data?.Configuration?.['tabId'] as string || '';\n }\n\n /**\n * Re-home this component to a different tab (cache reattach across tab\n * ids). Replaces the reactive param subscription with one bound to the\n * new tab and resets the duplicate-delivery key so the new tab's\n * CURRENT params apply as a fresh mount would — the replay-on-subscribe\n * delivers them immediately.\n */\n public RebindTabId(tabId: string): void {\n if (this.getTabId() === tabId) {\n return;\n }\n this.reboundTabId = tabId;\n this._lastDeliveredParamsKey = null;\n this.setupInitialParamDelivery();\n }\n\n private getQueryParamUpdateGuard(): TabQueryParamUpdateGuard {\n const config = this.Data?.Configuration || {};\n return {\n resourceType: config['resourceType'] as string | undefined,\n driverClass: (config['resourceTypeDriverClass'] || config['driverClass']) as string | undefined,\n recordId: (this.Data?.ResourceRecordID || config['recordId']) as string | undefined,\n navItemName: config['navItemName'] as string | undefined,\n entity: (config['Entity'] || config['entity']) as string | undefined\n };\n }\n\n protected NotifyLoadComplete() {\n this._loadComplete = true;\n if (this._loadCompleteWatchdog) {\n clearTimeout(this._loadCompleteWatchdog);\n this._loadCompleteWatchdog = null;\n }\n if (this._loadCompleteEvent) {\n this._loadCompleteEvent();\n }\n // Load completion almost always follows async work (RunView/fetch continuations) that\n // Angular's tick will NOT pick up on its own for dynamically-attached views — re-render\n // must be requested explicitly. See RefreshView() for the full explanation.\n this.RefreshView();\n }\n\n protected NotifyLoadStarted() {\n this._loadStarted = true;\n if (this._loadStartedEvent) {\n this._loadStartedEvent();\n }\n this.RefreshView();\n }\n\n /**\n * Marks this component's view (and its ancestors) as needing change detection and schedules\n * a tick — the reliable way for a resource component to re-render after updating state from\n * async work (RunView/RunQuery continuations, timers, websocket pushes, RxJS subscriptions).\n *\n * WHY THIS EXISTS: the Explorer shell hosts resource components dynamically — `createComponent()`\n * + `ApplicationRef.attachView()` — because tab content mounts into Golden Layout / cached DOM\n * containers, not into an Angular template. Since Angular 18's change-detection scheduler\n * rework (Explorer is on Angular 21), `ApplicationRef.tick()` only refreshes attached views\n * that are FLAGGED dirty; a root-level attached view whose component mutates plain fields from\n * an async continuation is never flagged, so the view silently never re-renders — even though\n * the component uses default (non-OnPush) change detection (issue #3106).\n *\n * The framework calls this automatically from NotifyLoadStarted() / NotifyLoadComplete(), so\n * the standard load lifecycle re-renders without any subclass action. Call it yourself after\n * any LATER async state change that must reach the DOM outside those signals.\n */\n protected RefreshView(): void {\n // Null on lightweight non-view instances (background title resolution)\n this.changeDetectorRef?.markForCheck();\n }\n\n\n\n /**\n * Call this to notify the tab system that the resource's display name has changed.\n * The tab container will update the tab title and browser title accordingly.\n */\n protected NotifyDisplayNameChanged(newName: string): void {\n if (this._displayNameChangedEvent) {\n this._displayNameChangedEvent(newName);\n }\n }\n\n protected ResourceRecordSaved(resourceRecordEntity: BaseEntity) {\n // Use ToURLSegment, NOT ToString. ResourceRecordID is consumed by\n // EntityRecordResource.GetPrimaryKey → CompositeKey.LoadFromURLSegment, which\n // expects URL-segment format (e.g. \"ID|<uuid>\"). ToString produces \"ID=<uuid>\",\n // which LoadFromURLSegment treats as a raw value and double-prefixes —\n // breaking any code path that re-reads this field after save.\n this.Data.ResourceRecordID = resourceRecordEntity.PrimaryKey.ToURLSegment();\n if (this._resourceRecordSavedEvent) {\n this._resourceRecordSavedEvent(resourceRecordEntity);\n }\n }\n\n /**\n * Ask the host shell to close/dismiss this resource (typically: close the tab).\n * Called by subclasses that hosted a form that emitted a 'dismiss' navigation\n * event — most often, a brand-new record where the user clicked Discard. The\n * record was never saved, the form is empty, and leaving it open serves no\n * purpose. The tab-container listens for this and closes the workspace tab.\n */\n protected NotifyCloseRequested(): void {\n if (this._resourceCloseRequestedEvent) {\n this._resourceCloseRequestedEvent();\n }\n }\n\n abstract GetResourceDisplayName(data: ResourceData): Promise<string>\n\n abstract GetResourceIconClass(data: ResourceData): Promise<string>\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { OnDestroy } from '@angular/core';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* The SHELL mobile chrome breakpoint. Matches the `@media (max-width: 768px)`
|
|
6
|
+
* blocks in shell.component.css that flip the hamburger/drawer chrome on —
|
|
7
|
+
* TS consumers (records mobile mode, the drawer's Records pill) must engage
|
|
8
|
+
* at exactly the same width or the shell shows mobile chrome while the
|
|
9
|
+
* records surface still renders its desktop strip (or vice versa).
|
|
10
|
+
*
|
|
11
|
+
* NOTE: the newer ui-components chrome (left-nav drawer, filter-popover
|
|
12
|
+
* sheet, page-body) breaks at 700px — that's component-local chrome with its
|
|
13
|
+
* own constant. This one is the shell's.
|
|
14
|
+
*/
|
|
15
|
+
export declare const EXPLORER_MOBILE_BREAKPOINT_PX = 768;
|
|
16
|
+
/**
|
|
17
|
+
* Reactive "is the shell in mobile chrome?" signal.
|
|
18
|
+
*
|
|
19
|
+
* matchMedia's `change` event fires OUTSIDE the Angular zone; the handler
|
|
20
|
+
* re-enters via zone.run (same pattern as mj-filter-popover) so template
|
|
21
|
+
* flips driven by the emission are seen by change detection. Zoneless
|
|
22
|
+
* subscribers should still call SafeDetectChanges after mutating their own
|
|
23
|
+
* state in the subscription.
|
|
24
|
+
*/
|
|
25
|
+
export declare class ExplorerBreakpointService implements OnDestroy {
|
|
26
|
+
private zone;
|
|
27
|
+
private query;
|
|
28
|
+
private isMobile$;
|
|
29
|
+
private onChange;
|
|
30
|
+
constructor();
|
|
31
|
+
/** Current value, synchronous. */
|
|
32
|
+
get IsMobile(): boolean;
|
|
33
|
+
/** Emits the current value on subscribe, then every breakpoint crossing. */
|
|
34
|
+
get IsMobile$(): Observable<boolean>;
|
|
35
|
+
ngOnDestroy(): void;
|
|
36
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ExplorerBreakpointService, never>;
|
|
37
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<ExplorerBreakpointService>;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=explorer-breakpoint.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explorer-breakpoint.service.d.ts","sourceRoot":"","sources":["../../src/lib/explorer-breakpoint.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,SAAS,EAAU,MAAM,eAAe,CAAC;AACtE,OAAO,EAAmB,UAAU,EAAE,MAAM,MAAM,CAAC;;AAEnD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,6BAA6B,MAAM,CAAC;AAEjD;;;;;;;;GAQG;AACH,qBACa,yBAA0B,YAAW,SAAS;IACzD,OAAO,CAAC,IAAI,CAAkB;IAE9B,OAAO,CAAC,KAAK,CAGF;IAEX,OAAO,CAAC,SAAS,CAA8D;IAE/E,OAAO,CAAC,QAAQ,CAEd;;IAMF,kCAAkC;IAClC,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED,4EAA4E;IAC5E,IAAW,SAAS,IAAI,UAAU,CAAC,OAAO,CAAC,CAE1C;IAID,WAAW,IAAI,IAAI;yCA9BR,yBAAyB;6CAAzB,yBAAyB;CAiCrC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Injectable, NgZone, inject } from '@angular/core';
|
|
2
|
+
import { BehaviorSubject } from 'rxjs';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* The SHELL mobile chrome breakpoint. Matches the `@media (max-width: 768px)`
|
|
6
|
+
* blocks in shell.component.css that flip the hamburger/drawer chrome on —
|
|
7
|
+
* TS consumers (records mobile mode, the drawer's Records pill) must engage
|
|
8
|
+
* at exactly the same width or the shell shows mobile chrome while the
|
|
9
|
+
* records surface still renders its desktop strip (or vice versa).
|
|
10
|
+
*
|
|
11
|
+
* NOTE: the newer ui-components chrome (left-nav drawer, filter-popover
|
|
12
|
+
* sheet, page-body) breaks at 700px — that's component-local chrome with its
|
|
13
|
+
* own constant. This one is the shell's.
|
|
14
|
+
*/
|
|
15
|
+
export const EXPLORER_MOBILE_BREAKPOINT_PX = 768;
|
|
16
|
+
/**
|
|
17
|
+
* Reactive "is the shell in mobile chrome?" signal.
|
|
18
|
+
*
|
|
19
|
+
* matchMedia's `change` event fires OUTSIDE the Angular zone; the handler
|
|
20
|
+
* re-enters via zone.run (same pattern as mj-filter-popover) so template
|
|
21
|
+
* flips driven by the emission are seen by change detection. Zoneless
|
|
22
|
+
* subscribers should still call SafeDetectChanges after mutating their own
|
|
23
|
+
* state in the subscription.
|
|
24
|
+
*/
|
|
25
|
+
export class ExplorerBreakpointService {
|
|
26
|
+
zone = inject(NgZone);
|
|
27
|
+
query = typeof window !== 'undefined' && typeof window.matchMedia === 'function'
|
|
28
|
+
? window.matchMedia(`(max-width: ${EXPLORER_MOBILE_BREAKPOINT_PX}px)`)
|
|
29
|
+
: null;
|
|
30
|
+
isMobile$ = new BehaviorSubject(this.query?.matches ?? false);
|
|
31
|
+
onChange = (e) => {
|
|
32
|
+
this.zone.run(() => this.isMobile$.next(e.matches));
|
|
33
|
+
};
|
|
34
|
+
constructor() {
|
|
35
|
+
this.query?.addEventListener('change', this.onChange);
|
|
36
|
+
}
|
|
37
|
+
/** Current value, synchronous. */
|
|
38
|
+
get IsMobile() {
|
|
39
|
+
return this.isMobile$.value;
|
|
40
|
+
}
|
|
41
|
+
/** Emits the current value on subscribe, then every breakpoint crossing. */
|
|
42
|
+
get IsMobile$() {
|
|
43
|
+
return this.isMobile$.asObservable();
|
|
44
|
+
}
|
|
45
|
+
// Root providers are destroyed on ApplicationRef teardown (tests,
|
|
46
|
+
// multi-bootstrap) — release the matchMedia listener with them
|
|
47
|
+
ngOnDestroy() {
|
|
48
|
+
this.query?.removeEventListener('change', this.onChange);
|
|
49
|
+
}
|
|
50
|
+
static ɵfac = function ExplorerBreakpointService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ExplorerBreakpointService)(); };
|
|
51
|
+
static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: ExplorerBreakpointService, factory: ExplorerBreakpointService.ɵfac, providedIn: 'root' });
|
|
52
|
+
}
|
|
53
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ExplorerBreakpointService, [{
|
|
54
|
+
type: Injectable,
|
|
55
|
+
args: [{ providedIn: 'root' }]
|
|
56
|
+
}], () => [], null); })();
|
|
57
|
+
//# sourceMappingURL=explorer-breakpoint.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explorer-breakpoint.service.js","sourceRoot":"","sources":["../../src/lib/explorer-breakpoint.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAa,MAAM,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,eAAe,EAAc,MAAM,MAAM,CAAC;;AAEnD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,CAAC;AAEjD;;;;;;;;GAQG;AAEH,MAAM,OAAO,yBAAyB;IAC5B,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAEtB,KAAK,GACX,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,UAAU;QACtE,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,eAAe,6BAA6B,KAAK,CAAC;QACtE,CAAC,CAAC,IAAI,CAAC;IAEH,SAAS,GAAG,IAAI,eAAe,CAAU,IAAI,CAAC,KAAK,EAAE,OAAO,IAAI,KAAK,CAAC,CAAC;IAEvE,QAAQ,GAAG,CAAC,CAAsB,EAAQ,EAAE;QAClD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC;IAEF;QACE,IAAI,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxD,CAAC;IAED,kCAAkC;IAClC,IAAW,QAAQ;QACjB,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;IAC9B,CAAC;IAED,4EAA4E;IAC5E,IAAW,SAAS;QAClB,OAAO,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,CAAC;IACvC,CAAC;IAED,kEAAkE;IAClE,+DAA+D;IAC/D,WAAW;QACT,IAAI,CAAC,KAAK,EAAE,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3D,CAAC;mHAhCU,yBAAyB;gEAAzB,yBAAyB,WAAzB,yBAAyB,mBADZ,MAAM;;iFACnB,yBAAyB;cADrC,UAAU;eAAC,EAAE,UAAU,EAAE,MAAM,EAAE","sourcesContent":["import { Injectable, NgZone, OnDestroy, inject } from '@angular/core';\nimport { BehaviorSubject, Observable } from 'rxjs';\n\n/**\n * The SHELL mobile chrome breakpoint. Matches the `@media (max-width: 768px)`\n * blocks in shell.component.css that flip the hamburger/drawer chrome on —\n * TS consumers (records mobile mode, the drawer's Records pill) must engage\n * at exactly the same width or the shell shows mobile chrome while the\n * records surface still renders its desktop strip (or vice versa).\n *\n * NOTE: the newer ui-components chrome (left-nav drawer, filter-popover\n * sheet, page-body) breaks at 700px — that's component-local chrome with its\n * own constant. This one is the shell's.\n */\nexport const EXPLORER_MOBILE_BREAKPOINT_PX = 768;\n\n/**\n * Reactive \"is the shell in mobile chrome?\" signal.\n *\n * matchMedia's `change` event fires OUTSIDE the Angular zone; the handler\n * re-enters via zone.run (same pattern as mj-filter-popover) so template\n * flips driven by the emission are seen by change detection. Zoneless\n * subscribers should still call SafeDetectChanges after mutating their own\n * state in the subscription.\n */\n@Injectable({ providedIn: 'root' })\nexport class ExplorerBreakpointService implements OnDestroy {\n private zone = inject(NgZone);\n\n private query: MediaQueryList | null =\n typeof window !== 'undefined' && typeof window.matchMedia === 'function'\n ? window.matchMedia(`(max-width: ${EXPLORER_MOBILE_BREAKPOINT_PX}px)`)\n : null;\n\n private isMobile$ = new BehaviorSubject<boolean>(this.query?.matches ?? false);\n\n private onChange = (e: MediaQueryListEvent): void => {\n this.zone.run(() => this.isMobile$.next(e.matches));\n };\n\n constructor() {\n this.query?.addEventListener('change', this.onChange);\n }\n\n /** Current value, synchronous. */\n public get IsMobile(): boolean {\n return this.isMobile$.value;\n }\n\n /** Emits the current value on subscribe, then every breakpoint crossing. */\n public get IsMobile$(): Observable<boolean> {\n return this.isMobile$.asObservable();\n }\n\n // Root providers are destroyed on ApplicationRef teardown (tests,\n // multi-bootstrap) — release the matchMedia listener with them\n ngOnDestroy(): void {\n this.query?.removeEventListener('change', this.onChange);\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"home-pin.service.d.ts","sourceRoot":"","sources":["../../src/lib/home-pin.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAGvC,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;;AAKxF;;;;;GAKG;AACH,qBACa,iBAAiB;IAC5B,+FAA+F;IAC/F,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,4BAA4B,CAAQ;IAE5D;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAQ;IAEnD,mDAAmD;IACnD,SAAgB,KAAK,uCAAgD;IAErE,OAAO,CAAC,MAAM,CAAS;IAMvB;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB/B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IASjC;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO;IAkBvC;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAM9B;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAQzD;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,iBAAiB,EAAE,GAAG,IAAI;IAS5C;;OAEG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO;IAIxE;;OAEG;IACH,OAAO,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,iBAAiB,GAAG,SAAS;IAQ7F;;OAEG;IACH,gBAAgB,IAAI,iBAAiB,EAAE;IAIvC;;OAEG;IACH,SAAS,IAAI,MAAM,EAAE;IAcrB;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAUtD;;;;;OAKG;IACG,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IA4BzE;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAgCnB,OAAO,CAAC,eAAe;
|
|
1
|
+
{"version":3,"file":"home-pin.service.d.ts","sourceRoot":"","sources":["../../src/lib/home-pin.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAGvC,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;;AAKxF;;;;;GAKG;AACH,qBACa,iBAAiB;IAC5B,+FAA+F;IAC/F,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,4BAA4B,CAAQ;IAE5D;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAQ;IAEnD,mDAAmD;IACnD,SAAgB,KAAK,uCAAgD;IAErE,OAAO,CAAC,MAAM,CAAS;IAMvB;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB/B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IASjC;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO;IAkBvC;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAM9B;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAQzD;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,iBAAiB,EAAE,GAAG,IAAI;IAS5C;;OAEG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO;IAIxE;;OAEG;IACH,OAAO,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,iBAAiB,GAAG,SAAS;IAQ7F;;OAEG;IACH,gBAAgB,IAAI,iBAAiB,EAAE;IAIvC;;OAEG;IACH,SAAS,IAAI,MAAM,EAAE;IAcrB;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAUtD;;;;;OAKG;IACG,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IA4BzE;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAgCnB,OAAO,CAAC,eAAe;IA6CvB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,UAAU;yCArSP,iBAAiB;6CAAjB,iBAAiB;CAiT7B"}
|
|
@@ -228,8 +228,6 @@ export class HomeAppPinService {
|
|
|
228
228
|
return pin.Configuration['viewId'] === config['viewId'];
|
|
229
229
|
case 'Queries':
|
|
230
230
|
return pin.Configuration['queryId'] === config['queryId'];
|
|
231
|
-
case 'Reports':
|
|
232
|
-
return pin.Configuration['reportId'] === config['reportId'];
|
|
233
231
|
case 'Records':
|
|
234
232
|
return pin.Configuration['Entity'] === config['Entity']
|
|
235
233
|
&& pin.Configuration['recordId'] === config['recordId'];
|