@smartbit4all/ng-client 6.0.0 → 6.0.2
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/esm2022/lib/smart-client/smart-component-api-client.mjs +12 -6
- package/esm2022/lib/smart-component-layout/smart-component-layout.component.mjs +5 -2
- package/esm2022/lib/smart-tree/smarttree-generic.service.mjs +6 -3
- package/esm2022/lib/smart-tree/smarttree.component.mjs +3 -3
- package/esm2022/lib/view-context/smart-embedded-slot.directive.mjs +4 -2
- package/esm2022/lib/view-context/smart-view-context.service.mjs +51 -18
- package/esm2022/lib/view-context/view-slot-resolution.mjs +1 -1
- package/fesm2022/smartbit4all-ng-client.mjs +75 -28
- package/fesm2022/smartbit4all-ng-client.mjs.map +1 -1
- package/lib/view-context/smart-view-context.service.d.ts +2 -1
- package/lib/view-context/view-slot-resolution.d.ts +12 -0
- package/package.json +1 -1
- package/smartbit4all-ng-client-6.0.2.tgz +0 -0
- package/smartbit4all-ng-client-6.0.0.tgz +0 -0
|
@@ -25,4 +25,4 @@ export function resolveSlot(view) {
|
|
|
25
25
|
containerId: ROUTER_SLOT_ID,
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
28
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmlldy1zbG90LXJlc29sdXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9zbWFydC1uZy1jbGllbnQvc3JjL2xpYi92aWV3LWNvbnRleHQvdmlldy1zbG90LXJlc29sdXRpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBRWhEOzJGQUMyRjtBQUMzRixNQUFNLENBQUMsTUFBTSxtQkFBbUIsR0FBRyxVQUFVLENBQUM7QUFFOUMsaUdBQWlHO0FBQ2pHLE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxlQUFlLENBQUM7QUFvQjlDOzs7R0FHRztBQUNILE1BQU0sVUFBVSxXQUFXLENBQUMsSUFBYztJQUN4QyxJQUFJLElBQUksQ0FBQyxJQUFJLEtBQUssUUFBUSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQ3BDLElBQUksQ0FBQyxJQUFJLENBQUMsYUFBYSxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQzdDLE9BQU8sQ0FBQyxLQUFLLENBQ1gsa0JBQWtCLElBQUksQ0FBQyxRQUFRLG1FQUFtRTtnQkFDaEcsc0JBQXNCLElBQUksQ0FBQyxhQUFhLG1CQUFtQixJQUFJLENBQUMsV0FBVyxJQUFJLENBQ2xGLENBQUM7UUFDSixDQUFDO1FBQ0QsT0FBTyxFQUFFLGFBQWEsRUFBRSxJQUFJLENBQUMsYUFBYyxFQUFFLFdBQVcsRUFBRSxJQUFJLENBQUMsV0FBWSxFQUFFLENBQUM7SUFDaEYsQ0FBQztJQUNELElBQUksSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ3JCLE9BQU8sQ0FBQyxLQUFLLENBQ1gsZ0JBQWdCLElBQUksQ0FBQyxRQUFRLHNCQUFzQixJQUFJLENBQUMsV0FBVyxTQUFTO1lBQzFFLDBEQUEwRCxjQUFjLElBQUksQ0FDL0UsQ0FBQztJQUNKLENBQUM7SUFDRCxPQUFPO1FBQ0wsYUFBYSxFQUFFLElBQUksQ0FBQyxhQUFhLElBQUksbUJBQW1CO1FBQ3hELFdBQVcsRUFBRSxjQUFjO0tBQzVCLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgVmlld0RhdGEgfSBmcm9tICcuL2FwaS9tb2RlbC92aWV3RGF0YSc7XG5pbXBvcnQgeyBWaWV3VHlwZSB9IGZyb20gJy4vYXBpL21vZGVsL3ZpZXdUeXBlJztcblxuLyoqIFNlbnRpbmVsIGNvbnRhaW5lclV1aWQgZm9yIHRoZSBob3N0IHJvb3Qgc2xvdCwgcmVnaXN0ZXJlZCBieSB0aGUgc2hlbGwncyByb290XG4gKiAgU21hcnRFbWJlZGRlZFNsb3REaXJlY3RpdmU7IG9wZW5WaWV3IG1hcHMgYSBOT1JNQUwgdmlldyB3aXRoIG5vIGNvbnRhaW5lclV1aWQgdG8gaXQuICovXG5leHBvcnQgY29uc3QgUk9PVF9DT05UQUlORVJfVVVJRCA9ICdfX1JPT1RfXyc7XG5cbi8qKiBDb252ZW50aW9uYWwgY29udGFpbmVySWQgZm9yIHRoZSBzaW5nbGUgXCJjdXJyZW50IHBhZ2VcIiBzbG90IHRoYXQgcmVwbGFjZXMgPHJvdXRlci1vdXRsZXQ+LiAqL1xuZXhwb3J0IGNvbnN0IFJPVVRFUl9TTE9UX0lEID0gJ3JvdXRlci1vdXRsZXQnO1xuXG4vKipcbiAqIExpZmVjeWNsZSBjbGFzcyBvZiBhbiBlbWJlZGRlZCBzbG90LCBkZWNsYXJlZCBieSB3aG9ldmVyIHJlZ2lzdGVycyBpdDpcbiAqICAtICdzdGFibGUnOiBhIFNtYXJ0RW1iZWRkZWRTbG90RGlyZWN0aXZlIHNsb3QgKGUuZy4gdGhlIGhvc3Qgcm9vdCBzbG90IG9yIGFcbiAqICAgIHBhZ2UncyByb3V0ZXItb3V0bGV0IHNsb3QpLiBSZWdpc3RlcmVkIG9uY2UgaW4gbmdPbkluaXQgYW5kIGxpdmVzIE9VVFNJREUgYW55XG4gKiAgICBsYXlvdXQgKm5nRm9yLCBzbyBpdCBzdXJ2aXZlcyBhIHBhcmVudCBsYXlvdXQgcmUtcmVuZGVyLiBJdHMgbW91bnRlZCBjaGlsZCBtdXN0XG4gKiAgICBOT1QgYmUgZGV0YWNoZWQgYnkgdGhlIGxheW91dCBkZXRhY2gvY2xlYW51cCBkYW5jZS5cbiAqICAtICdsYXlvdXQnOiBhIFNtYXJ0Q29tcG9uZW50TGF5b3V0Q29tcG9uZW50IEVNQkVEREVEX1NMT1Qgd2lkZ2V0IHNsb3QuIExpdmVzXG4gKiAgICBJTlNJREUgdGhlIHJlLXJlbmRlcmluZyBsYXlvdXQgKm5nRm9yIHRyZWUsIHNvIGEgdG9wLWxldmVsIGxheW91dCByZS1yZW5kZXJcbiAqICAgIGRlc3Ryb3lzIGFuZCByZWNyZWF0ZXMgaXQ7IGl0cyBjaGlsZCBtdXN0IGJlIGRldGFjaGVkIGZpcnN0IHRvIHN1cnZpdmUgdGhlXG4gKiAgICAqbmdGb3IgZGVzdHJ1Y3Rpb24sIHRoZW4gcmVhdHRhY2hlcyBvbiByZS1yZWdpc3RyYXRpb24uICgjMjg4NzApXG4gKi9cbmV4cG9ydCB0eXBlIFNsb3RLaW5kID0gJ3N0YWJsZScgfCAnbGF5b3V0JztcblxuZXhwb3J0IGludGVyZmFjZSBSZXNvbHZlZFNsb3Qge1xuICBjb250YWluZXJVdWlkOiBzdHJpbmc7XG4gIGNvbnRhaW5lcklkOiBzdHJpbmc7XG59XG5cbi8qKlxuICogUmVzb2x2ZXMgdGhlIGVtYmVkZGVkIHNsb3QgZm9yIE5PUk1BTCBhbmQgRU1CRURERUQgdmlld3Mgb25seS5cbiAqIERJQUxPRyB2aWV3cyBhcmUgaGFuZGxlZCBzZXBhcmF0ZWx5IHVwc3RyZWFtIGluIG9wZW5WaWV3IGFuZCBtdXN0IG5vdCBiZSBwYXNzZWQgaGVyZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVTbG90KHZpZXc6IFZpZXdEYXRhKTogUmVzb2x2ZWRTbG90IHtcbiAgaWYgKHZpZXcudHlwZSA9PT0gVmlld1R5cGUuRU1CRURERUQpIHtcbiAgICBpZiAoIXZpZXcuY29udGFpbmVyVXVpZCB8fCAhdmlldy5jb250YWluZXJJZCkge1xuICAgICAgY29uc29sZS5lcnJvcihcbiAgICAgICAgYEVNQkVEREVEIHZpZXcgJyR7dmlldy52aWV3TmFtZX0nIGlzIG1hbGZvcm1lZDogYm90aCBjb250YWluZXJVdWlkIGFuZCBjb250YWluZXJJZCBhcmUgcmVxdWlyZWQsIGAgK1xuICAgICAgICAgIGBnb3QgY29udGFpbmVyVXVpZD0nJHt2aWV3LmNvbnRhaW5lclV1aWR9JywgY29udGFpbmVySWQ9JyR7dmlldy5jb250YWluZXJJZH0nLmAsXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4geyBjb250YWluZXJVdWlkOiB2aWV3LmNvbnRhaW5lclV1aWQhLCBjb250YWluZXJJZDogdmlldy5jb250YWluZXJJZCEgfTtcbiAgfVxuICBpZiAodmlldy5jb250YWluZXJJZCkge1xuICAgIGNvbnNvbGUuZXJyb3IoXG4gICAgICBgTk9STUFMIHZpZXcgJyR7dmlldy52aWV3TmFtZX0nIGhhcyBjb250YWluZXJJZCAnJHt2aWV3LmNvbnRhaW5lcklkfScgc2V0OyBgICtcbiAgICAgICAgYGNvbnRhaW5lcklkIGltcGxpZXMgYW4gRU1CRURERUQgdmlldy4gRmFsbGluZyBiYWNrIHRvICcke1JPVVRFUl9TTE9UX0lEfScuYCxcbiAgICApO1xuICB9XG4gIHJldHVybiB7XG4gICAgY29udGFpbmVyVXVpZDogdmlldy5jb250YWluZXJVdWlkID8/IFJPT1RfQ09OVEFJTkVSX1VVSUQsXG4gICAgY29udGFpbmVySWQ6IFJPVVRFUl9TTE9UX0lELFxuICB9O1xufVxuIl19
|
|
@@ -3578,13 +3578,13 @@ class SmartViewContextService {
|
|
|
3578
3578
|
unfollow(uuid) {
|
|
3579
3579
|
this.smartApiClients.delete(uuid);
|
|
3580
3580
|
}
|
|
3581
|
-
registerEmbeddedSlot(containerUuid, containerId, vcRef) {
|
|
3581
|
+
registerEmbeddedSlot(containerUuid, containerId, vcRef, kind) {
|
|
3582
3582
|
let innerMap = this.embeddedContainers.get(containerUuid);
|
|
3583
3583
|
if (!innerMap) {
|
|
3584
3584
|
innerMap = new Map();
|
|
3585
3585
|
this.embeddedContainers.set(containerUuid, innerMap);
|
|
3586
3586
|
}
|
|
3587
|
-
innerMap.set(containerId, vcRef);
|
|
3587
|
+
innerMap.set(containerId, { vcRef, kind });
|
|
3588
3588
|
// Reattach embedded views that were detached before layout re-render
|
|
3589
3589
|
const existing = this.embeddedViewRefs.filter((ref) => ref.containerUuid === containerUuid && ref.containerId === containerId);
|
|
3590
3590
|
for (const ref of existing) {
|
|
@@ -3610,26 +3610,43 @@ class SmartViewContextService {
|
|
|
3610
3610
|
if (child.componentRef.hostView.destroyed) {
|
|
3611
3611
|
continue;
|
|
3612
3612
|
}
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3613
|
+
// Only detach views living in a re-rendering LAYOUT slot. A STABLE directive
|
|
3614
|
+
// slot (router-outlet) lives outside the layout *ngFor and survives the parent
|
|
3615
|
+
// re-render, so its child needs no detach protection — detaching it here is
|
|
3616
|
+
// what produced the false-orphan destroy in multi-level NORMAL nesting
|
|
3617
|
+
// (Home -> Main -> page): Main got swept up by Home's layout dance. (#28870)
|
|
3618
|
+
const entry = this.embeddedContainers.get(containerUuid)?.get(child.containerId);
|
|
3619
|
+
if (!entry || entry.kind === 'stable') {
|
|
3620
|
+
continue;
|
|
3621
|
+
}
|
|
3622
|
+
const idx = entry.vcRef.indexOf(child.componentRef.hostView);
|
|
3623
|
+
if (idx >= 0) {
|
|
3624
|
+
entry.vcRef.detach(idx);
|
|
3619
3625
|
}
|
|
3620
3626
|
}
|
|
3621
3627
|
}
|
|
3622
3628
|
cleanupOrphanedEmbeddedViews(containerUuid) {
|
|
3623
|
-
const
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
}
|
|
3629
|
+
const refs = this.embeddedViewRefs.filter((ref) => ref.containerUuid === containerUuid);
|
|
3630
|
+
const orphaned = [];
|
|
3631
|
+
for (const ref of refs) {
|
|
3627
3632
|
if (ref.componentRef.hostView.destroyed) {
|
|
3628
|
-
|
|
3633
|
+
orphaned.push(ref);
|
|
3634
|
+
continue;
|
|
3629
3635
|
}
|
|
3630
3636
|
const vcRef = this.getEmbeddedSlot(containerUuid, ref.containerId);
|
|
3631
|
-
|
|
3632
|
-
|
|
3637
|
+
if (!vcRef) {
|
|
3638
|
+
// The slot is truly gone (its layout host was torn down and not recreated):
|
|
3639
|
+
// a real orphan to destroy.
|
|
3640
|
+
orphaned.push(ref);
|
|
3641
|
+
continue;
|
|
3642
|
+
}
|
|
3643
|
+
// Slot still registered but its view was detached by detachAllEmbeddedViews and
|
|
3644
|
+
// never reattached, because the *ngFor reused the slot host (only a sibling
|
|
3645
|
+
// widget changed) so no re-register fired. Reattach so every detach is paired.
|
|
3646
|
+
if (vcRef.indexOf(ref.componentRef.hostView) < 0) {
|
|
3647
|
+
vcRef.insert(ref.componentRef.hostView);
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3633
3650
|
for (const ref of orphaned) {
|
|
3634
3651
|
if (!ref.componentRef.hostView.destroyed) {
|
|
3635
3652
|
ref.componentRef.destroy();
|
|
@@ -3679,7 +3696,7 @@ class SmartViewContextService {
|
|
|
3679
3696
|
}
|
|
3680
3697
|
}
|
|
3681
3698
|
getEmbeddedSlot(containerUuid, containerId) {
|
|
3682
|
-
return this.embeddedContainers.get(containerUuid)?.get(containerId);
|
|
3699
|
+
return this.embeddedContainers.get(containerUuid)?.get(containerId)?.vcRef;
|
|
3683
3700
|
}
|
|
3684
3701
|
/**
|
|
3685
3702
|
* Create the view's component into its resolved slot. Returns false and queues
|
|
@@ -3689,13 +3706,29 @@ class SmartViewContextService {
|
|
|
3689
3706
|
const { containerUuid, containerId } = resolveSlot(view);
|
|
3690
3707
|
const vcRef = this.getEmbeddedSlot(containerUuid, containerId);
|
|
3691
3708
|
if (!vcRef) {
|
|
3709
|
+
// Slot not registered yet: queue the view and drain it when its slot registers
|
|
3710
|
+
// (registerEmbeddedSlot). The synchronous detectChanges below renders a freshly
|
|
3711
|
+
// mounted parent's template in the same syncView pass, so a parent -> child ->
|
|
3712
|
+
// grandchild chain (Home -> Main -> page) registers every slot immediately and
|
|
3713
|
+
// the queue drains within the pass — even when restoreViews opens a child before
|
|
3714
|
+
// its parent. (#28870)
|
|
3692
3715
|
this.pendingEmbeddedViews.push(view);
|
|
3693
3716
|
return false;
|
|
3694
3717
|
}
|
|
3695
3718
|
const componentRef = vcRef.createComponent(component);
|
|
3696
3719
|
componentRef.instance.uuid = view.uuid;
|
|
3697
|
-
componentRef.instance.run();
|
|
3698
3720
|
this.embeddedViewRefs.push({ uuid: view.uuid, containerUuid, containerId, componentRef });
|
|
3721
|
+
// Render the freshly created component synchronously so its own embedded-slot
|
|
3722
|
+
// directives (the router-outlet slot of a nested NORMAL view) register their
|
|
3723
|
+
// containers immediately. This lets a parent -> child -> grandchild chain
|
|
3724
|
+
// (e.g. Home -> Main -> page) mount in a single synchronous syncView pass
|
|
3725
|
+
// instead of depending on a change-detection tick firing between each level.
|
|
3726
|
+
// Without it the child cannot resolve its slot, queues into
|
|
3727
|
+
// pendingEmbeddedViews, and the drain races the async updateViewContext
|
|
3728
|
+
// round-trips — intermittently leaving a parent OPENED in backend bookkeeping
|
|
3729
|
+
// but never mounted on the client, so its slot child stays blank. (#28870)
|
|
3730
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
3731
|
+
componentRef.instance.run();
|
|
3699
3732
|
return true;
|
|
3700
3733
|
}
|
|
3701
3734
|
setActionDescriptors(actionDescriptors) {
|
|
@@ -12153,7 +12186,9 @@ class SmartEmbeddedSlotDirective {
|
|
|
12153
12186
|
tryRegister() {
|
|
12154
12187
|
// console.log('[EmbeddedSlot] tryRegister', { containerId: this.containerId, containerUuid: this.containerUuid });
|
|
12155
12188
|
if (this.containerUuid && this.containerId) {
|
|
12156
|
-
|
|
12189
|
+
// A directive slot is STABLE: it registers once in ngOnInit and lives outside
|
|
12190
|
+
// any layout *ngFor, so it survives a parent layout re-render. (#28870)
|
|
12191
|
+
this.viewContext.registerEmbeddedSlot(this.containerUuid, this.containerId, this.vcRef, 'stable');
|
|
12157
12192
|
this.registered = true;
|
|
12158
12193
|
this.registeredContainerUuid = this.containerUuid;
|
|
12159
12194
|
this.registeredContainerId = this.containerId;
|
|
@@ -18377,8 +18412,11 @@ class SmarttreeGenericService extends SmarttreeService {
|
|
|
18377
18412
|
}
|
|
18378
18413
|
async syncTree() {
|
|
18379
18414
|
if (this.treeFromBackend === null || this.treeFromBackend === undefined) {
|
|
18380
|
-
//
|
|
18381
|
-
|
|
18415
|
+
// Expected transient: syncTree can be triggered (e.g. by a host dataChanged or a
|
|
18416
|
+
// push update) before the tree's first downloadTree() has populated treeFromBackend.
|
|
18417
|
+
// There is simply nothing to render yet, so this is a no-op, not an error. Kept at
|
|
18418
|
+
// debug level so it stays discoverable without polluting the console on every load.
|
|
18419
|
+
console.debug('syncTree skipped: treeFromBackend not downloaded yet');
|
|
18382
18420
|
return;
|
|
18383
18421
|
}
|
|
18384
18422
|
await this.cacheActionDesciptors(this.getAllNodes(this.treeFromBackend));
|
|
@@ -18705,11 +18743,11 @@ class SmartTreeComponent {
|
|
|
18705
18743
|
return `${cssClass}-${plusProperty}`;
|
|
18706
18744
|
}
|
|
18707
18745
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.11", ngImport: i0, type: SmartTreeComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
18708
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.11", type: SmartTreeComponent, selector: "smart-tree", inputs: { treeStyle: "treeStyle", treeService: "treeService" }, host: { properties: { "attr.data-testid": "this.testId" } }, viewQueries: [{ propertyName: "tree", first: true, predicate: ["tree"], descendants: true }, { propertyName: "trigger", predicate: MatMenuTrigger, descendants: true }], ngImport: i0, template: "<div class=\"smartTreeContainer\">\n <smart-ui-action-toolbar\n *ngIf=\"uiActionModels.length\"\n [uiActionModels]=\"uiActionModels\"\n ></smart-ui-action-toolbar>\n <mat-tree\n #tree\n *ngIf=\"treeData\"\n [dataSource]=\"dataSource\"\n [treeControl]=\"treeControl\"\n class=\"sm-tree\"\n >\n <mat-nested-tree-node\n *matTreeNodeDef=\"let node; when: hasChild\"\n matTreeNodeToggle=\"{{ getIfExpanded(node) }}\"\n [ngClass]=\"getClassesForTreeNode(node)\"\n [ngStyle]=\"getNodeStyle(node)\"\n >\n <div\n [ngStyle]=\"getNodePadding(node)\"\n [ngClass]=\"getInnerClassesForTreeNode(node)\"\n class=\"mat-tree-node sm-tree-node\"\n (click)=\"onNodeClick($event, node)\"\n >\n <button
|
|
18746
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.11", type: SmartTreeComponent, selector: "smart-tree", inputs: { treeStyle: "treeStyle", treeService: "treeService" }, host: { properties: { "attr.data-testid": "this.testId" } }, viewQueries: [{ propertyName: "tree", first: true, predicate: ["tree"], descendants: true }, { propertyName: "trigger", predicate: MatMenuTrigger, descendants: true }], ngImport: i0, template: "<div class=\"smartTreeContainer\">\n <smart-ui-action-toolbar\n *ngIf=\"uiActionModels.length\"\n [uiActionModels]=\"uiActionModels\"\n ></smart-ui-action-toolbar>\n <mat-tree\n #tree\n *ngIf=\"treeData\"\n [dataSource]=\"dataSource\"\n [treeControl]=\"treeControl\"\n class=\"sm-tree\"\n >\n <mat-nested-tree-node\n *matTreeNodeDef=\"let node; when: hasChild\"\n matTreeNodeToggle=\"{{ getIfExpanded(node) }}\"\n [ngClass]=\"getClassesForTreeNode(node)\"\n [ngStyle]=\"getNodeStyle(node)\"\n >\n <div\n [ngStyle]=\"getNodePadding(node)\"\n [ngClass]=\"getInnerClassesForTreeNode(node)\"\n class=\"mat-tree-node sm-tree-node\"\n (click)=\"onNodeClick($event, node)\"\n >\n <button\n mat-icon-button\n matTreeNodeToggle\n (click)=\"onOpenNode($event, node)\"\n [attr.aria-label]=\"'Toggle ' + node.name\"\n >\n <mat-icon class=\"mat-icon-rtl-mirror\">\n <div *ngIf=\"hasChild(node.level, node)\">\n {{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}\n </div>\n </mat-icon>\n </button>\n <smart-icon [icon]=\"node.icon\"> </smart-icon>\n <div class=\"sm-tree-row\" [ngClass]=\"node.classes\">\n <div class=\"sm-tree-row-caption\">\n {{ node.caption }}\n </div>\n <div class=\"sm-shortDescription-spacer\"></div>\n <div class=\"sm-tree-row-shortDescription\">\n {{ node.shortDescription }}\n </div>\n <div class=\"sm-shortDescription-button-spacer\"></div>\n <div *ngIf=\"node.button\" class=\"sm-tree-node-button\" [ngSwitch]=\"node.button.type\">\n <button\n (click)=\"customButtonClicked($event, node.button)\"\n *ngSwitchCase=\"smartTreeNodeButtonType.ICON\"\n mat-icon-button\n >\n <smart-icon title=\"{{ node.button.icon }}\" [icon]=\"node.button.icon\"></smart-icon>\n </button>\n <div *ngSwitchCase=\"smartTreeNodeButtonType.MENU\">\n <button\n mat-button\n [matMenuTriggerFor]=\"menu\"\n (click)=\"customButtonClicked($event, node.button, true)\"\n >\n <smart-icon *ngIf=\"node.button.icon\" [icon]=\"node.button.icon\"></smart-icon\n >{{ node.button.label }}\n </button>\n <mat-menu #menu=\"matMenu\">\n <button\n *ngFor=\"let button of node.button.menuItemButtons\"\n (click)=\"customButtonClicked($event, button, true)\"\n mat-menu-item\n [attr.data-testid]=\"button.code ?? null\"\n >\n <smart-icon *ngIf=\"button.icon\" [icon]=\"button.icon\"></smart-icon\n >{{ button.label }}\n </button>\n </mat-menu>\n </div>\n <button\n (click)=\"customButtonClicked($event, node.button)\"\n *ngSwitchCase=\"smartTreeNodeButtonType.NORMAL\"\n mat-raised-button\n >\n <smart-icon *ngIf=\"node.button.icon\" [icon]=\"node.button.icon\"></smart-icon>\n {{ node.button.icon }}\n </button>\n </div>\n </div>\n </div>\n <div\n [class.sm-tree-invisible]=\"!treeControl.isExpanded(node)\"\n [ngClass]=\"getClassesForTreeNodeChildren(node)\"\n role=\"group\"\n >\n <ng-container matTreeNodeOutlet></ng-container>\n </div>\n </mat-nested-tree-node>\n </mat-tree>\n <div *ngIf=\"!treeData\">\n <h3>\n {{ errorMessage }}\n </h3>\n </div>\n</div>\n", styles: [".smartTreeContainer{display:flex;flex-direction:column;gap:.5rem}.sm-tree-invisible{display:none}.sm-tree ul,.sm-tree li{margin-top:0;margin-bottom:0;list-style-type:none}.sm-tree div[role=group]>.mat-tree-node{padding-left:40px}.sm-tee-node{padding-left:40px}.sm-tree-node-name{padding-left:15px;padding-top:15px;display:flex;flex-direction:column}.sm-tree-node-name-row{padding-left:15px;padding-top:15px;display:flex;flex-direction:row;justify-content:space-between}.sm-tree-node-name-col{padding-left:15px;padding-top:15px;display:flex;flex-direction:column}.sm-tee-node-id{font-weight:lighter}.mat-tree-node:hover{cursor:pointer}::ng-deep .mat-icon-rtl-mirror{display:flex;flex-direction:row}.sm-tree-row{display:flex;flex-direction:row;flex:1;align-items:center}.sm-shortDescription-spacer{flex:1}\n"], dependencies: [{ kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1$2.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$2.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i5.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i11$1.MatNestedTreeNode, selector: "mat-nested-tree-node", inputs: ["matNestedTreeNode", "disabled", "tabIndex"], exportAs: ["matNestedTreeNode"] }, { kind: "directive", type: i11$1.MatTreeNodeDef, selector: "[matTreeNodeDef]", inputs: ["matTreeNodeDefWhen", "matTreeNode"] }, { kind: "directive", type: i11$1.MatTreeNodeToggle, selector: "[matTreeNodeToggle]", inputs: ["matTreeNodeToggleRecursive"] }, { kind: "component", type: i11$1.MatTree, selector: "mat-tree", exportAs: ["matTree"] }, { kind: "directive", type: i11$1.MatTreeNodeOutlet, selector: "[matTreeNodeOutlet]" }, { kind: "component", type: i5$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i5$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i5$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: SmartIconComponent, selector: "smart-icon", inputs: ["icon", "color", "imageResource"] }, { kind: "component", type: UiActionToolbarComponent, selector: "smart-ui-action-toolbar", inputs: ["uiActionModels", "uiActionDescriptorService", "id", "scrollOnWrap", "toolbarPropertes"] }] }); }
|
|
18709
18747
|
}
|
|
18710
18748
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.11", ngImport: i0, type: SmartTreeComponent, decorators: [{
|
|
18711
18749
|
type: Component,
|
|
18712
|
-
args: [{ selector: 'smart-tree', template: "<div class=\"smartTreeContainer\">\n <smart-ui-action-toolbar\n *ngIf=\"uiActionModels.length\"\n [uiActionModels]=\"uiActionModels\"\n ></smart-ui-action-toolbar>\n <mat-tree\n #tree\n *ngIf=\"treeData\"\n [dataSource]=\"dataSource\"\n [treeControl]=\"treeControl\"\n class=\"sm-tree\"\n >\n <mat-nested-tree-node\n *matTreeNodeDef=\"let node; when: hasChild\"\n matTreeNodeToggle=\"{{ getIfExpanded(node) }}\"\n [ngClass]=\"getClassesForTreeNode(node)\"\n [ngStyle]=\"getNodeStyle(node)\"\n >\n <div\n [ngStyle]=\"getNodePadding(node)\"\n [ngClass]=\"getInnerClassesForTreeNode(node)\"\n class=\"mat-tree-node sm-tree-node\"\n (click)=\"onNodeClick($event, node)\"\n >\n <button
|
|
18750
|
+
args: [{ selector: 'smart-tree', template: "<div class=\"smartTreeContainer\">\n <smart-ui-action-toolbar\n *ngIf=\"uiActionModels.length\"\n [uiActionModels]=\"uiActionModels\"\n ></smart-ui-action-toolbar>\n <mat-tree\n #tree\n *ngIf=\"treeData\"\n [dataSource]=\"dataSource\"\n [treeControl]=\"treeControl\"\n class=\"sm-tree\"\n >\n <mat-nested-tree-node\n *matTreeNodeDef=\"let node; when: hasChild\"\n matTreeNodeToggle=\"{{ getIfExpanded(node) }}\"\n [ngClass]=\"getClassesForTreeNode(node)\"\n [ngStyle]=\"getNodeStyle(node)\"\n >\n <div\n [ngStyle]=\"getNodePadding(node)\"\n [ngClass]=\"getInnerClassesForTreeNode(node)\"\n class=\"mat-tree-node sm-tree-node\"\n (click)=\"onNodeClick($event, node)\"\n >\n <button\n mat-icon-button\n matTreeNodeToggle\n (click)=\"onOpenNode($event, node)\"\n [attr.aria-label]=\"'Toggle ' + node.name\"\n >\n <mat-icon class=\"mat-icon-rtl-mirror\">\n <div *ngIf=\"hasChild(node.level, node)\">\n {{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}\n </div>\n </mat-icon>\n </button>\n <smart-icon [icon]=\"node.icon\"> </smart-icon>\n <div class=\"sm-tree-row\" [ngClass]=\"node.classes\">\n <div class=\"sm-tree-row-caption\">\n {{ node.caption }}\n </div>\n <div class=\"sm-shortDescription-spacer\"></div>\n <div class=\"sm-tree-row-shortDescription\">\n {{ node.shortDescription }}\n </div>\n <div class=\"sm-shortDescription-button-spacer\"></div>\n <div *ngIf=\"node.button\" class=\"sm-tree-node-button\" [ngSwitch]=\"node.button.type\">\n <button\n (click)=\"customButtonClicked($event, node.button)\"\n *ngSwitchCase=\"smartTreeNodeButtonType.ICON\"\n mat-icon-button\n >\n <smart-icon title=\"{{ node.button.icon }}\" [icon]=\"node.button.icon\"></smart-icon>\n </button>\n <div *ngSwitchCase=\"smartTreeNodeButtonType.MENU\">\n <button\n mat-button\n [matMenuTriggerFor]=\"menu\"\n (click)=\"customButtonClicked($event, node.button, true)\"\n >\n <smart-icon *ngIf=\"node.button.icon\" [icon]=\"node.button.icon\"></smart-icon\n >{{ node.button.label }}\n </button>\n <mat-menu #menu=\"matMenu\">\n <button\n *ngFor=\"let button of node.button.menuItemButtons\"\n (click)=\"customButtonClicked($event, button, true)\"\n mat-menu-item\n [attr.data-testid]=\"button.code ?? null\"\n >\n <smart-icon *ngIf=\"button.icon\" [icon]=\"button.icon\"></smart-icon\n >{{ button.label }}\n </button>\n </mat-menu>\n </div>\n <button\n (click)=\"customButtonClicked($event, node.button)\"\n *ngSwitchCase=\"smartTreeNodeButtonType.NORMAL\"\n mat-raised-button\n >\n <smart-icon *ngIf=\"node.button.icon\" [icon]=\"node.button.icon\"></smart-icon>\n {{ node.button.icon }}\n </button>\n </div>\n </div>\n </div>\n <div\n [class.sm-tree-invisible]=\"!treeControl.isExpanded(node)\"\n [ngClass]=\"getClassesForTreeNodeChildren(node)\"\n role=\"group\"\n >\n <ng-container matTreeNodeOutlet></ng-container>\n </div>\n </mat-nested-tree-node>\n </mat-tree>\n <div *ngIf=\"!treeData\">\n <h3>\n {{ errorMessage }}\n </h3>\n </div>\n</div>\n", styles: [".smartTreeContainer{display:flex;flex-direction:column;gap:.5rem}.sm-tree-invisible{display:none}.sm-tree ul,.sm-tree li{margin-top:0;margin-bottom:0;list-style-type:none}.sm-tree div[role=group]>.mat-tree-node{padding-left:40px}.sm-tee-node{padding-left:40px}.sm-tree-node-name{padding-left:15px;padding-top:15px;display:flex;flex-direction:column}.sm-tree-node-name-row{padding-left:15px;padding-top:15px;display:flex;flex-direction:row;justify-content:space-between}.sm-tree-node-name-col{padding-left:15px;padding-top:15px;display:flex;flex-direction:column}.sm-tee-node-id{font-weight:lighter}.mat-tree-node:hover{cursor:pointer}::ng-deep .mat-icon-rtl-mirror{display:flex;flex-direction:row}.sm-tree-row{display:flex;flex-direction:row;flex:1;align-items:center}.sm-shortDescription-spacer{flex:1}\n"] }]
|
|
18713
18751
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { trigger: [{
|
|
18714
18752
|
type: ViewChildren,
|
|
18715
18753
|
args: [MatMenuTrigger]
|
|
@@ -21839,7 +21877,10 @@ class SmartComponentLayoutComponent {
|
|
|
21839
21877
|
}
|
|
21840
21878
|
tryRegisterEmbeddedSlot() {
|
|
21841
21879
|
if (this.embeddedSlotContainerId && this.uuid && this._embeddedSlotVcRef) {
|
|
21842
|
-
|
|
21880
|
+
// An EMBEDDED_SLOT widget slot is LAYOUT-kind: it lives inside the re-rendering
|
|
21881
|
+
// layout *ngFor, so its child must be detached before a top-level re-render and
|
|
21882
|
+
// reattaches when this slot re-registers. (#28870)
|
|
21883
|
+
this.viewContext.registerEmbeddedSlot(this.uuid, this.embeddedSlotContainerId, this._embeddedSlotVcRef, 'layout');
|
|
21843
21884
|
}
|
|
21844
21885
|
}
|
|
21845
21886
|
updateUuid(newUuid) {
|
|
@@ -23387,9 +23428,15 @@ class SmartComponentApiClient {
|
|
|
23387
23428
|
this.getAllSmartGridComponents()
|
|
23388
23429
|
.filter((grid) => (grid?.smartGrid?.gridIdentifier ? true : false))
|
|
23389
23430
|
.forEach((grid) => widgets.set(grid.smartGrid.gridIdentifier, grid));
|
|
23431
|
+
// Key trees by their treeId (set in the service constructor), NOT by
|
|
23432
|
+
// smartTreeModel.identifier (only set after the first successful syncTree). A tree
|
|
23433
|
+
// that exists but has not finished its initial download would otherwise be absent
|
|
23434
|
+
// from the widget map, so a push update could not resolve it
|
|
23435
|
+
// ("Provided reference for <treeId> is undefined"). treeId equals the backend
|
|
23436
|
+
// widget identifier, so push routing matches regardless of download timing. (#28361)
|
|
23390
23437
|
this.getAllSmartTreeComponents()
|
|
23391
|
-
.filter((tree) =>
|
|
23392
|
-
.forEach((tree) => widgets.set(tree.
|
|
23438
|
+
.filter((tree) => !!tree?.treeId)
|
|
23439
|
+
.forEach((tree) => widgets.set(tree.treeId, tree));
|
|
23393
23440
|
this.getAllSmartFilterEditorContentComponents()
|
|
23394
23441
|
.filter((filter) => (filter.service.config.identifier ? true : false))
|
|
23395
23442
|
.forEach((filter) => widgets.set(filter.service.config.identifier, filter));
|
|
@@ -23841,12 +23888,12 @@ class SmartComponentApiClient {
|
|
|
23841
23888
|
}
|
|
23842
23889
|
createTreeService(treeId) {
|
|
23843
23890
|
const treeService = new SmarttreeGenericService(this.inject, this.pageName, treeId);
|
|
23891
|
+
// In the useQueryLists model the tree is routed via the SmartTreeComponent
|
|
23892
|
+
// QueryList + treeId (see getWidgets), so no manual widgets.set is needed; the
|
|
23893
|
+
// legacy non-QueryList path still registers it explicitly.
|
|
23844
23894
|
if (!this.useQueryLists) {
|
|
23845
23895
|
this.widgets.set(treeId, treeService);
|
|
23846
23896
|
}
|
|
23847
|
-
else {
|
|
23848
|
-
console.warn(`Added smartTree when useQueryLists === true (${treeId})`);
|
|
23849
|
-
}
|
|
23850
23897
|
return treeService;
|
|
23851
23898
|
}
|
|
23852
23899
|
addFileUploader(uiActionCode, fileFormats, maxSizeMb, isMultiple) {
|