@smartbit4all/ng-client 6.0.0 → 6.0.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/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 +49 -18
- package/esm2022/lib/view-context/view-slot-resolution.mjs +1 -1
- package/fesm2022/smartbit4all-ng-client.mjs +73 -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.1.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,41 @@ 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. A LAYOUT slot that came back re-registers and
|
|
3640
|
+
// reattaches its child (registerEmbeddedSlot); a STABLE slot is never detached
|
|
3641
|
+
// in the first place. So a still-registered slot here always holds its view —
|
|
3642
|
+
// no reattach branch is needed (that branch only ever patched the stable
|
|
3643
|
+
// false-orphan, which detachAllEmbeddedViews no longer creates). (#28870)
|
|
3644
|
+
orphaned.push(ref);
|
|
3645
|
+
continue;
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3633
3648
|
for (const ref of orphaned) {
|
|
3634
3649
|
if (!ref.componentRef.hostView.destroyed) {
|
|
3635
3650
|
ref.componentRef.destroy();
|
|
@@ -3679,7 +3694,7 @@ class SmartViewContextService {
|
|
|
3679
3694
|
}
|
|
3680
3695
|
}
|
|
3681
3696
|
getEmbeddedSlot(containerUuid, containerId) {
|
|
3682
|
-
return this.embeddedContainers.get(containerUuid)?.get(containerId);
|
|
3697
|
+
return this.embeddedContainers.get(containerUuid)?.get(containerId)?.vcRef;
|
|
3683
3698
|
}
|
|
3684
3699
|
/**
|
|
3685
3700
|
* Create the view's component into its resolved slot. Returns false and queues
|
|
@@ -3689,13 +3704,29 @@ class SmartViewContextService {
|
|
|
3689
3704
|
const { containerUuid, containerId } = resolveSlot(view);
|
|
3690
3705
|
const vcRef = this.getEmbeddedSlot(containerUuid, containerId);
|
|
3691
3706
|
if (!vcRef) {
|
|
3707
|
+
// Slot not registered yet: queue the view and drain it when its slot registers
|
|
3708
|
+
// (registerEmbeddedSlot). The synchronous detectChanges below renders a freshly
|
|
3709
|
+
// mounted parent's template in the same syncView pass, so a parent -> child ->
|
|
3710
|
+
// grandchild chain (Home -> Main -> page) registers every slot immediately and
|
|
3711
|
+
// the queue drains within the pass — even when restoreViews opens a child before
|
|
3712
|
+
// its parent. (#28870)
|
|
3692
3713
|
this.pendingEmbeddedViews.push(view);
|
|
3693
3714
|
return false;
|
|
3694
3715
|
}
|
|
3695
3716
|
const componentRef = vcRef.createComponent(component);
|
|
3696
3717
|
componentRef.instance.uuid = view.uuid;
|
|
3697
|
-
componentRef.instance.run();
|
|
3698
3718
|
this.embeddedViewRefs.push({ uuid: view.uuid, containerUuid, containerId, componentRef });
|
|
3719
|
+
// Render the freshly created component synchronously so its own embedded-slot
|
|
3720
|
+
// directives (the router-outlet slot of a nested NORMAL view) register their
|
|
3721
|
+
// containers immediately. This lets a parent -> child -> grandchild chain
|
|
3722
|
+
// (e.g. Home -> Main -> page) mount in a single synchronous syncView pass
|
|
3723
|
+
// instead of depending on a change-detection tick firing between each level.
|
|
3724
|
+
// Without it the child cannot resolve its slot, queues into
|
|
3725
|
+
// pendingEmbeddedViews, and the drain races the async updateViewContext
|
|
3726
|
+
// round-trips — intermittently leaving a parent OPENED in backend bookkeeping
|
|
3727
|
+
// but never mounted on the client, so its slot child stays blank. (#28870)
|
|
3728
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
3729
|
+
componentRef.instance.run();
|
|
3699
3730
|
return true;
|
|
3700
3731
|
}
|
|
3701
3732
|
setActionDescriptors(actionDescriptors) {
|
|
@@ -12153,7 +12184,9 @@ class SmartEmbeddedSlotDirective {
|
|
|
12153
12184
|
tryRegister() {
|
|
12154
12185
|
// console.log('[EmbeddedSlot] tryRegister', { containerId: this.containerId, containerUuid: this.containerUuid });
|
|
12155
12186
|
if (this.containerUuid && this.containerId) {
|
|
12156
|
-
|
|
12187
|
+
// A directive slot is STABLE: it registers once in ngOnInit and lives outside
|
|
12188
|
+
// any layout *ngFor, so it survives a parent layout re-render. (#28870)
|
|
12189
|
+
this.viewContext.registerEmbeddedSlot(this.containerUuid, this.containerId, this.vcRef, 'stable');
|
|
12157
12190
|
this.registered = true;
|
|
12158
12191
|
this.registeredContainerUuid = this.containerUuid;
|
|
12159
12192
|
this.registeredContainerId = this.containerId;
|
|
@@ -18377,8 +18410,11 @@ class SmarttreeGenericService extends SmarttreeService {
|
|
|
18377
18410
|
}
|
|
18378
18411
|
async syncTree() {
|
|
18379
18412
|
if (this.treeFromBackend === null || this.treeFromBackend === undefined) {
|
|
18380
|
-
//
|
|
18381
|
-
|
|
18413
|
+
// Expected transient: syncTree can be triggered (e.g. by a host dataChanged or a
|
|
18414
|
+
// push update) before the tree's first downloadTree() has populated treeFromBackend.
|
|
18415
|
+
// There is simply nothing to render yet, so this is a no-op, not an error. Kept at
|
|
18416
|
+
// debug level so it stays discoverable without polluting the console on every load.
|
|
18417
|
+
console.debug('syncTree skipped: treeFromBackend not downloaded yet');
|
|
18382
18418
|
return;
|
|
18383
18419
|
}
|
|
18384
18420
|
await this.cacheActionDesciptors(this.getAllNodes(this.treeFromBackend));
|
|
@@ -18705,11 +18741,11 @@ class SmartTreeComponent {
|
|
|
18705
18741
|
return `${cssClass}-${plusProperty}`;
|
|
18706
18742
|
}
|
|
18707
18743
|
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
|
|
18744
|
+
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
18745
|
}
|
|
18710
18746
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.11", ngImport: i0, type: SmartTreeComponent, decorators: [{
|
|
18711
18747
|
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
|
|
18748
|
+
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
18749
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { trigger: [{
|
|
18714
18750
|
type: ViewChildren,
|
|
18715
18751
|
args: [MatMenuTrigger]
|
|
@@ -21839,7 +21875,10 @@ class SmartComponentLayoutComponent {
|
|
|
21839
21875
|
}
|
|
21840
21876
|
tryRegisterEmbeddedSlot() {
|
|
21841
21877
|
if (this.embeddedSlotContainerId && this.uuid && this._embeddedSlotVcRef) {
|
|
21842
|
-
|
|
21878
|
+
// An EMBEDDED_SLOT widget slot is LAYOUT-kind: it lives inside the re-rendering
|
|
21879
|
+
// layout *ngFor, so its child must be detached before a top-level re-render and
|
|
21880
|
+
// reattaches when this slot re-registers. (#28870)
|
|
21881
|
+
this.viewContext.registerEmbeddedSlot(this.uuid, this.embeddedSlotContainerId, this._embeddedSlotVcRef, 'layout');
|
|
21843
21882
|
}
|
|
21844
21883
|
}
|
|
21845
21884
|
updateUuid(newUuid) {
|
|
@@ -23387,9 +23426,15 @@ class SmartComponentApiClient {
|
|
|
23387
23426
|
this.getAllSmartGridComponents()
|
|
23388
23427
|
.filter((grid) => (grid?.smartGrid?.gridIdentifier ? true : false))
|
|
23389
23428
|
.forEach((grid) => widgets.set(grid.smartGrid.gridIdentifier, grid));
|
|
23429
|
+
// Key trees by their treeId (set in the service constructor), NOT by
|
|
23430
|
+
// smartTreeModel.identifier (only set after the first successful syncTree). A tree
|
|
23431
|
+
// that exists but has not finished its initial download would otherwise be absent
|
|
23432
|
+
// from the widget map, so a push update could not resolve it
|
|
23433
|
+
// ("Provided reference for <treeId> is undefined"). treeId equals the backend
|
|
23434
|
+
// widget identifier, so push routing matches regardless of download timing. (#28361)
|
|
23390
23435
|
this.getAllSmartTreeComponents()
|
|
23391
|
-
.filter((tree) =>
|
|
23392
|
-
.forEach((tree) => widgets.set(tree.
|
|
23436
|
+
.filter((tree) => !!tree?.treeId)
|
|
23437
|
+
.forEach((tree) => widgets.set(tree.treeId, tree));
|
|
23393
23438
|
this.getAllSmartFilterEditorContentComponents()
|
|
23394
23439
|
.filter((filter) => (filter.service.config.identifier ? true : false))
|
|
23395
23440
|
.forEach((filter) => widgets.set(filter.service.config.identifier, filter));
|
|
@@ -23841,12 +23886,12 @@ class SmartComponentApiClient {
|
|
|
23841
23886
|
}
|
|
23842
23887
|
createTreeService(treeId) {
|
|
23843
23888
|
const treeService = new SmarttreeGenericService(this.inject, this.pageName, treeId);
|
|
23889
|
+
// In the useQueryLists model the tree is routed via the SmartTreeComponent
|
|
23890
|
+
// QueryList + treeId (see getWidgets), so no manual widgets.set is needed; the
|
|
23891
|
+
// legacy non-QueryList path still registers it explicitly.
|
|
23844
23892
|
if (!this.useQueryLists) {
|
|
23845
23893
|
this.widgets.set(treeId, treeService);
|
|
23846
23894
|
}
|
|
23847
|
-
else {
|
|
23848
|
-
console.warn(`Added smartTree when useQueryLists === true (${treeId})`);
|
|
23849
|
-
}
|
|
23850
23895
|
return treeService;
|
|
23851
23896
|
}
|
|
23852
23897
|
addFileUploader(uiActionCode, fileFormats, maxSizeMb, isMultiple) {
|