This is an automated email from the ASF dual-hosted git repository.

zehnder pushed a commit to branch 
3116-refactor-staticruntimeresolvabletreeinput-into-smaller-subcomponents
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to 
refs/heads/3116-refactor-staticruntimeresolvabletreeinput-into-smaller-subcomponents
 by this push:
     new 0676e07e58 refactor(#3116): Extract 
static-runtime-resolvable-tree-input component
0676e07e58 is described below

commit 0676e07e583e49fa8ffa4a49773ab5572840c8eb
Author: Philipp Zehnder <[email protected]>
AuthorDate: Mon Aug 12 10:03:09 2024 +0200

    refactor(#3116): Extract static-runtime-resolvable-tree-input component
---
 ui/src/app/core-ui/core-ui.module.ts               |   2 +
 ...ic-runtime-resolvable-tree-input.component.html | 166 +--------------------
 ...atic-runtime-resolvable-tree-input.component.ts | 106 +++----------
 .../static-tree-input-browse-nodes.component.html  | 154 +++++++++++++++++++
 .../static-tree-input-browse-nodes.component.scss  |  26 ++++
 .../static-tree-input-browse-nodes.component.ts    | 149 ++++++++++++++++++
 .../static-tree-input-service.service.ts           |  18 +++
 7 files changed, 379 insertions(+), 242 deletions(-)

diff --git a/ui/src/app/core-ui/core-ui.module.ts 
b/ui/src/app/core-ui/core-ui.module.ts
index db9918893f..ce16cf73c1 100644
--- a/ui/src/app/core-ui/core-ui.module.ts
+++ b/ui/src/app/core-ui/core-ui.module.ts
@@ -111,6 +111,7 @@ import { MultiStepStatusIndicatorComponent } from 
'./multi-step-status-indicator
 import { PipelineOperationStatusComponent } from 
'./pipeline/pipeline-operation-status/pipeline-operation-status.component';
 import { StaticTreeInputButtonMenuComponent } from 
'./static-properties/static-runtime-resolvable-tree-input/static-tree-input-button-menu/static-tree-input-button-menu.component';
 import { StaticTreeInputSelectedNodesComponent } from 
'./static-properties/static-runtime-resolvable-tree-input/static-tree-input-selected-nodes/static-tree-input-selected-nodes.component';
+import { StaticTreeInputBrowseNodesComponent } from 
'./static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component';
 
 @NgModule({
     imports: [
@@ -190,6 +191,7 @@ import { StaticTreeInputSelectedNodesComponent } from 
'./static-properties/stati
         StaticRuntimeResolvableGroupComponent,
         StaticRuntimeResolvableOneOfInputComponent,
         StaticRuntimeResolvableTreeInputComponent,
+        StaticTreeInputBrowseNodesComponent,
         StaticSlideToggleComponent,
         ErrorHintComponent,
         AddToCollectionComponent,
diff --git 
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.html
 
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.html
index 4e5a88f149..4975e8413c 100644
--- 
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.html
+++ 
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.html
@@ -27,167 +27,17 @@
     <div fxLayout="column" *ngIf="error" class="mt-10">
         <sp-exception-message [message]="errorMessage"></sp-exception-message>
     </div>
+
     <div fxLayout="row" fxLayoutGap="15px">
         <div fxFlex="30" class="tree-input-section" fxLayout="column">
-            <div class="tree-input-header" fxLayoutAlign="start center">
-                <div fxLayout="row" fxFlex="100">
-                    <div fxFlex fxLayoutAlign="start 
center"><b>Browse</b></div>
-                    <div fxLayoutAlign="end center">
-                        <button
-                            mat-icon-button
-                            color="accent"
-                            (click)="largeView = !largeView"
-                        >
-                            <mat-icon 
*ngIf="!largeView">open_in_full</mat-icon>
-                            <mat-icon *ngIf="largeView"
-                                >close_fullscreen
-                            </mat-icon>
-                        </button>
-                    </div>
-                </div>
-            </div>
-            <mat-tree
-                [dataSource]="dataSource"
-                [treeControl]="treeControl"
-                #tree
-                class="sp-tree"
-                [ngClass]="
-                    largeView ? 'tree-large-height' : 'tree-normal-height'
-                "
+            <sp-static-tree-input-browse-nodes
+                #staticTreeInputBrowseNodesComponent
+                [selectedNodeMetadata]="selectedNodeMetadata"
+                [staticProperty]="staticProperty"
+                
(loadOptionsFromRestApiEmitter)="loadOptionsFromRestApi($event)"
+                (performValidationEmitter)="performValidation()"
             >
-                <mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle>
-                    <div
-                        [ngClass]="
-                            selectedNodeId === node.internalNodeName
-                                ? 'selected-node'
-                                : 'node'
-                        "
-                        fxLayoutAlign="start center"
-                        [attr.data-cy]="'tree-node-' + node.nodeName"
-                    >
-                        <i class="material-icons color-primary pr-5">wifi</i>
-                        <i
-                            class="material-icons pr-5 icon-button"
-                            *ngIf="node.dataNode && !isNodeSelected(node)"
-                            (click)="addNode(node)"
-                            [attr.data-cy]="'select-' + node.nodeName"
-                            matTooltip="Add node"
-                            >add_circle</i
-                        >
-                        <i
-                            class="material-icons pr-5 icon-button"
-                            *ngIf="node.dataNode && isNodeSelected(node)"
-                            (click)="removeNode(node)"
-                            matTooltip="Remove node"
-                            >remove_circle</i
-                        >
-                        <i
-                            class="material-icons pr-5 icon-button"
-                            *ngIf="selectedNodeId !== node.internalNodeName"
-                            (click)="
-                                selectedNodeMetadata = node.nodeMetadata;
-                                selectedNodeId = node.internalNodeName
-                            "
-                            matTooltip="Show details"
-                            >visibility</i
-                        >
-                        <i
-                            class="material-icons pr-5 icon-button"
-                            *ngIf="selectedNodeId === node.internalNodeName"
-                            (click)="
-                                selectedNodeMetadata = undefined;
-                                selectedNodeId = undefined
-                            "
-                            matTooltip="Show details"
-                            >visibility_off</i
-                        >
-                        <span class="pr-5">{{ node.nodeName }}</span>
-                    </div>
-                </mat-tree-node>
-                <mat-nested-tree-node
-                    *matTreeNodeDef="let node; when: hasChild"
-                >
-                    <div class="mat-tree-node">
-                        <button
-                            mat-icon-button
-                            matTreeNodeToggle
-                            [attr.data-cy]="'expand-' + node.nodeName"
-                            [attr.aria-label]="'Toggle ' + node.nodeName"
-                            (click)="
-                                loadChildren(node, 
treeControl.isExpanded(node))
-                            "
-                        >
-                            <mat-icon class="mat-icon-rtl-mirror">
-                                {{
-                                    treeControl.isExpanded(node)
-                                        ? 'expand_more'
-                                        : 'chevron_right'
-                                }}
-                            </mat-icon>
-                        </button>
-                        <div
-                            [ngClass]="
-                                selectedNodeId === node.internalNodeName
-                                    ? 'selected-node'
-                                    : 'node'
-                            "
-                            fxLayoutAlign="start center"
-                        >
-                            <i
-                                class="material-icons color-primary pr-5"
-                                *ngIf="!treeControl.isExpanded(node)"
-                                >folder</i
-                            >
-                            <i
-                                class="material-icons color-primary pr-5"
-                                *ngIf="treeControl.isExpanded(node)"
-                                >folder_open</i
-                            >
-                            <i
-                                class="material-icons pr-5 icon-button"
-                                *ngIf="
-                                    treeControl.isExpanded(node) &&
-                                    hasDataChildren(node)
-                                "
-                                matTooltip="Add all direct children"
-                                (click)="addAllDirectChildren(node)"
-                                >add_circle</i
-                            >
-                            <i
-                                class="material-icons pr-5 icon-button"
-                                *ngIf="selectedNodeId !== 
node.internalNodeName"
-                                (click)="
-                                    selectedNodeMetadata = node.nodeMetadata;
-                                    selectedNodeId = node.internalNodeName
-                                "
-                                matTooltip="Show details"
-                                >visibility</i
-                            >
-                            <i
-                                class="material-icons pr-5 icon-button"
-                                *ngIf="selectedNodeId === 
node.internalNodeName"
-                                (click)="
-                                    selectedNodeMetadata = undefined;
-                                    selectedNodeId = undefined
-                                "
-                                matTooltip="Show details"
-                                >visibility_off</i
-                            >
-                            <span class="node-name pr-5"
-                                >&nbsp;{{ node.nodeName }}</span
-                            >
-                        </div>
-                    </div>
-                    <div
-                        [class.sp-tree-invisible]="
-                            !treeControl.isExpanded(node)
-                        "
-                        role="group"
-                    >
-                        <ng-container matTreeNodeOutlet></ng-container>
-                    </div>
-                </mat-nested-tree-node>
-            </mat-tree>
+            </sp-static-tree-input-browse-nodes>
         </div>
         <div fxFlex="40" class="tree-input-section" fxLayout="column">
             <div class="tree-input-header" fxLayoutAlign="start center">
diff --git 
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.ts
 
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.ts
index 1be730b01d..57e5005af7 100644
--- 
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.ts
+++ 
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-runtime-resolvable-tree-input.component.ts
@@ -24,9 +24,9 @@ import {
     TreeInputNode,
 } from '@streampipes/platform-services';
 import { RuntimeResolvableService } from 
'../static-runtime-resolvable-input/runtime-resolvable.service';
-import { NestedTreeControl } from '@angular/cdk/tree';
-import { MatTree, MatTreeNestedDataSource } from '@angular/material/tree';
 import { UntypedFormControl } from '@angular/forms';
+import { StaticTreeInputServiceService } from 
'./static-tree-input-service.service';
+import { StaticTreeInputBrowseNodesComponent } from 
'./static-tree-input-browse-nodes/static-tree-input-browse-nodes.component';
 
 @Component({
     selector: 'sp-static-runtime-resolvable-tree-input',
@@ -37,28 +37,19 @@ export class StaticRuntimeResolvableTreeInputComponent
     extends 
BaseRuntimeResolvableInput<RuntimeResolvableTreeInputStaticProperty>
     implements OnInit
 {
-    treeControl = new NestedTreeControl<TreeInputNode>(node => node.children);
-    dataSource = new MatTreeNestedDataSource<TreeInputNode>();
-
     selectedNodeMetadata: Record<string, string>;
-    selectedNodeId: string;
-
-    largeView = false;
 
-    @ViewChild('tree') tree: MatTree<TreeInputNode>;
+    @ViewChild('staticTreeInputBrowseNodesComponent')
+    staticTreeInputBrowseNodesComponent: StaticTreeInputBrowseNodesComponent;
 
-    constructor(runtimeResolvableService: RuntimeResolvableService) {
+    constructor(
+        runtimeResolvableService: RuntimeResolvableService,
+        private staticTreeInputServiceService: StaticTreeInputServiceService,
+    ) {
         super(runtimeResolvableService);
     }
 
-    hasChild = (_: number, node: TreeInputNode) => !node.dataNode;
-
     ngOnInit(): void {
-        this.treeControl = new NestedTreeControl<TreeInputNode>(
-            node => node.children,
-        );
-        this.dataSource = new MatTreeNestedDataSource<TreeInputNode>();
-
         if (
             this.staticProperty.nodes.length === 0 &&
             (!this.staticProperty.dependsOn ||
@@ -66,7 +57,9 @@ export class StaticRuntimeResolvableTreeInputComponent
         ) {
             this.loadOptionsFromRestApi();
         } else if (this.staticProperty.nodes.length > 0) {
-            this.dataSource.data = this.staticProperty.nodes;
+            this.staticTreeInputBrowseNodesComponent.updateNodes(
+                this.staticProperty.nodes,
+            );
             this.showOptions = true;
         }
         super.onInit();
@@ -97,12 +90,12 @@ export class StaticRuntimeResolvableTreeInputComponent
             }
         } else {
             this.staticProperty.nodes = staticProperty.nodes;
-            this.dataSource.data = this.staticProperty.nodes;
+            this.staticTreeInputBrowseNodesComponent.updateNodes(
+                this.staticProperty.nodes,
+            );
         }
-        const data = this.dataSource.data.slice();
-        this.dataSource.data = null;
-        this.dataSource = new MatTreeNestedDataSource<TreeInputNode>();
-        this.dataSource.data = [...data];
+        this.staticTreeInputBrowseNodesComponent.refreshTree();
+
         this.performValidation();
     }
 
@@ -130,7 +123,7 @@ export class StaticRuntimeResolvableTreeInputComponent
 
     afterErrorReceived() {
         this.staticProperty.nodes = [];
-        this.dataSource.data = [];
+        this.staticTreeInputBrowseNodesComponent.updateNodes([]);
         this.performValidation();
     }
 
@@ -138,7 +131,7 @@ export class StaticRuntimeResolvableTreeInputComponent
         this.staticProperty.nextBaseNodeToResolve = undefined;
         this.staticProperty.selectedNodesInternalNames = [];
         this.staticProperty.latestFetchedNodes = [];
-        this.dataSource.data = [];
+        this.staticTreeInputBrowseNodesComponent.updateNodes([]);
         this.loadOptionsFromRestApi();
     }
 
@@ -146,66 +139,11 @@ export class StaticRuntimeResolvableTreeInputComponent
         this.loadOptionsFromRestApi();
     }
 
-    loadChildren(node: TreeInputNode, expanded: boolean): void {
-        this.staticProperty.nextBaseNodeToResolve = node.internalNodeName;
-        if (expanded) {
-            this.loadOptionsFromRestApi(node);
-        }
-    }
-
-    addNode(node: TreeInputNode) {
-        node.selected = true;
-        this.staticProperty.selectedNodesInternalNames.push(
-            node.internalNodeName,
-        );
-        this.performValidation();
-    }
-
-    addAllDirectChildren(node: TreeInputNode) {
-        node.children.forEach(child => {
-            if (child.dataNode && !this.existsSelectedNode(child)) {
-                this.staticProperty.selectedNodesInternalNames.push(
-                    child.internalNodeName,
-                );
-            }
-        });
-        this.performValidation();
-    }
-
-    existsSelectedNode(node: TreeInputNode) {
-        return (
-            this.staticProperty.selectedNodesInternalNames.find(
-                nodeName => nodeName === node.internalNodeName,
-            ) !== undefined
-        );
-    }
-
     removeSelectedNode(selectedNodeInternalId: string): void {
-        const index = this.getSelectedNodeIndex(selectedNodeInternalId);
-        this.staticProperty.selectedNodesInternalNames.splice(index, 1);
-    }
-
-    removeNode(node: TreeInputNode) {
-        node.selected = false;
-        const index = this.getSelectedNodeIndex(node.internalNodeName);
-        this.staticProperty.selectedNodesInternalNames.splice(index, 1);
-        this.performValidation();
-    }
-
-    isNodeSelected(node: TreeInputNode) {
-        return this.getSelectedNodeIndex(node.internalNodeName) > -1;
-    }
-
-    getSelectedNodeIndex(internalNodeName: string) {
-        return this.staticProperty.selectedNodesInternalNames.indexOf(
-            internalNodeName,
-        );
-    }
-
-    hasDataChildren(node: TreeInputNode) {
-        return (
-            node.children.length > 0 &&
-            node.children.find(c => c.dataNode) !== undefined
+        const index = this.staticTreeInputServiceService.getSelectedNodeIndex(
+            this.staticProperty,
+            selectedNodeInternalId,
         );
+        this.staticProperty.selectedNodesInternalNames.splice(index, 1);
     }
 }
diff --git 
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.html
 
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.html
new file mode 100644
index 0000000000..23d7deb1cc
--- /dev/null
+++ 
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.html
@@ -0,0 +1,154 @@
+<!--
+~ Licensed to the Apache Software Foundation (ASF) under one or more
+~ contributor license agreements.  See the NOTICE file distributed with
+~ this work for additional information regarding copyright ownership.
+~ The ASF licenses this file to You under the Apache License, Version 2.0
+~ (the "License"); you may not use this file except in compliance with
+~ the License.  You may obtain a copy of the License at
+~
+~    http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+~
+-->
+
+<div class="tree-input-header" fxLayoutAlign="start center">
+    <div fxLayout="row" fxFlex="100">
+        <div fxFlex fxLayoutAlign="start center"><b>Browse</b></div>
+        <div fxLayoutAlign="end center">
+            <button
+                mat-icon-button
+                color="accent"
+                (click)="largeView = !largeView"
+            >
+                <mat-icon *ngIf="!largeView">open_in_full</mat-icon>
+                <mat-icon *ngIf="largeView">close_fullscreen </mat-icon>
+            </button>
+        </div>
+    </div>
+</div>
+
+<mat-tree
+    [dataSource]="dataSource"
+    [treeControl]="treeControl"
+    #tree
+    class="sp-tree"
+    [ngClass]="largeView ? 'tree-large-height' : 'tree-normal-height'"
+>
+    <mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle>
+        <div
+            [ngClass]="
+                selectedNodeId === node.internalNodeName
+                    ? 'selected-node'
+                    : 'node'
+            "
+            fxLayoutAlign="start center"
+            [attr.data-cy]="'tree-node-' + node.nodeName"
+        >
+            <i class="material-icons color-primary pr-5">wifi</i>
+            <i
+                class="material-icons pr-5 icon-button"
+                *ngIf="node.dataNode && !isNodeSelected(node)"
+                (click)="addNode(node)"
+                [attr.data-cy]="'select-' + node.nodeName"
+                matTooltip="Add node"
+                >add_circle</i
+            >
+            <i
+                class="material-icons pr-5 icon-button"
+                *ngIf="node.dataNode && isNodeSelected(node)"
+                (click)="removeNode(node)"
+                matTooltip="Remove node"
+                >remove_circle</i
+            >
+            <i
+                class="material-icons pr-5 icon-button"
+                *ngIf="selectedNodeId !== node.internalNodeName"
+                (click)="
+                    selectedNodeMetadata = node.nodeMetadata;
+                    selectedNodeId = node.internalNodeName
+                "
+                matTooltip="Show details"
+                >visibility</i
+            >
+            <i
+                class="material-icons pr-5 icon-button"
+                *ngIf="selectedNodeId === node.internalNodeName"
+                (click)="
+                    selectedNodeMetadata = undefined; selectedNodeId = 
undefined
+                "
+                matTooltip="Show details"
+                >visibility_off</i
+            >
+            <span class="pr-5">{{ node.nodeName }}</span>
+        </div>
+    </mat-tree-node>
+    <mat-nested-tree-node *matTreeNodeDef="let node; when: hasChild">
+        <div class="mat-tree-node">
+            <button
+                mat-icon-button
+                matTreeNodeToggle
+                [attr.data-cy]="'expand-' + node.nodeName"
+                [attr.aria-label]="'Toggle ' + node.nodeName"
+                (click)="loadChildren(node, treeControl.isExpanded(node))"
+            >
+                <mat-icon class="mat-icon-rtl-mirror">
+                    {{
+                        treeControl.isExpanded(node)
+                            ? 'expand_more'
+                            : 'chevron_right'
+                    }}
+                </mat-icon>
+            </button>
+            <div fxLayoutAlign="start center">
+                <i
+                    class="material-icons color-primary pr-5"
+                    *ngIf="!treeControl.isExpanded(node)"
+                    >folder</i
+                >
+                <i
+                    class="material-icons color-primary pr-5"
+                    *ngIf="treeControl.isExpanded(node)"
+                    >folder_open</i
+                >
+                <i
+                    class="material-icons pr-5 icon-button"
+                    *ngIf="
+                        treeControl.isExpanded(node) && hasDataChildren(node)
+                    "
+                    matTooltip="Add all direct children"
+                    (click)="addAllDirectChildren(node)"
+                    >add_circle</i
+                >
+                <i
+                    class="material-icons pr-5 icon-button"
+                    *ngIf="selectedNodeId !== node.internalNodeName"
+                    (click)="
+                        selectedNodeMetadata = node.nodeMetadata;
+                        selectedNodeId = node.internalNodeName
+                    "
+                    matTooltip="Show details"
+                    >visibility</i
+                >
+                <i
+                    class="material-icons pr-5 icon-button"
+                    *ngIf="selectedNodeId === node.internalNodeName"
+                    (click)="
+                        selectedNodeMetadata = undefined;
+                        selectedNodeId = undefined
+                    "
+                    matTooltip="Show details"
+                    >visibility_off</i
+                >
+                <span class="node-name pr-5">&nbsp;{{ node.nodeName }}</span>
+            </div>
+        </div>
+        <div *ngIf="treeControl.isExpanded(node)" role="group">
+            <ng-container matTreeNodeOutlet></ng-container>
+        </div>
+    </mat-nested-tree-node>
+</mat-tree>
diff --git 
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.scss
 
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.scss
new file mode 100644
index 0000000000..949b23cb05
--- /dev/null
+++ 
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.scss
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+.tree-normal-height {
+    min-height: 300px;
+    max-height: 300px;
+}
+
+.tree-large-height {
+    min-height: 800px;
+}
diff --git 
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.ts
 
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.ts
new file mode 100644
index 0000000000..93dbf9671b
--- /dev/null
+++ 
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component.ts
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+import {
+    Component,
+    EventEmitter,
+    Input,
+    OnInit,
+    Output,
+    ViewChild,
+} from '@angular/core';
+import { NestedTreeControl } from '@angular/cdk/tree';
+import {
+    RuntimeResolvableTreeInputStaticProperty,
+    TreeInputNode,
+} from '@streampipes/platform-services';
+import { MatTree, MatTreeNestedDataSource } from '@angular/material/tree';
+import { StaticTreeInputServiceService } from 
'../static-tree-input-service.service';
+
+@Component({
+    selector: 'sp-static-tree-input-browse-nodes',
+    templateUrl: './static-tree-input-browse-nodes.component.html',
+    styleUrls: [
+        './static-tree-input-browse-nodes.component.scss',
+        '../static-runtime-resolvable-tree-input.component.scss',
+    ],
+})
+export class StaticTreeInputBrowseNodesComponent implements OnInit {
+    @Input()
+    selectedNodeMetadata: Record<string, string>;
+
+    @Input()
+    staticProperty: RuntimeResolvableTreeInputStaticProperty;
+
+    @Output()
+    performValidationEmitter: EventEmitter<void> = new EventEmitter<void>();
+
+    @Output()
+    loadOptionsFromRestApiEmitter: EventEmitter<TreeInputNode> =
+        new EventEmitter<TreeInputNode>();
+
+    @ViewChild('tree')
+    tree: MatTree<TreeInputNode>;
+
+    largeView = false;
+    treeControl = new NestedTreeControl<TreeInputNode>(node => node.children);
+    dataSource = new MatTreeNestedDataSource<TreeInputNode>();
+
+    selectedNodeId: string;
+
+    hasChild = (_: number, node: TreeInputNode) => !node.dataNode;
+
+    constructor(
+        private staticTreeInputServiceService: StaticTreeInputServiceService,
+    ) {}
+
+    ngOnInit(): void {
+        this.treeControl = new NestedTreeControl<TreeInputNode>(
+            node => node.children,
+        );
+
+        this.dataSource = new MatTreeNestedDataSource<TreeInputNode>();
+    }
+
+    updateNodes(nodes: TreeInputNode[]) {
+        this.dataSource.data = nodes;
+    }
+
+    refreshTree() {
+        const data = this.dataSource.data.slice();
+        this.dataSource.data = null;
+        this.dataSource = new MatTreeNestedDataSource<TreeInputNode>();
+        this.dataSource.data = [...data];
+    }
+
+    loadChildren(node: TreeInputNode, expanded: boolean): void {
+        this.staticProperty.nextBaseNodeToResolve = node.internalNodeName;
+        if (expanded) {
+            this.loadOptionsFromRestApiEmitter.emit(node);
+        }
+    }
+
+    addNode(node: TreeInputNode) {
+        node.selected = true;
+        this.staticProperty.selectedNodesInternalNames.push(
+            node.internalNodeName,
+        );
+        this.performValidationEmitter.emit();
+    }
+
+    addAllDirectChildren(node: TreeInputNode) {
+        node.children.forEach(child => {
+            if (child.dataNode && !this.existsSelectedNode(child)) {
+                this.staticProperty.selectedNodesInternalNames.push(
+                    child.internalNodeName,
+                );
+            }
+        });
+        this.performValidationEmitter.emit();
+    }
+
+    removeNode(node: TreeInputNode) {
+        node.selected = false;
+        const index = this.staticTreeInputServiceService.getSelectedNodeIndex(
+            this.staticProperty,
+            node.internalNodeName,
+        );
+        this.staticProperty.selectedNodesInternalNames.splice(index, 1);
+        this.performValidationEmitter.emit();
+    }
+
+    isNodeSelected(node: TreeInputNode) {
+        return (
+            this.staticTreeInputServiceService.getSelectedNodeIndex(
+                this.staticProperty,
+                node.internalNodeName,
+            ) > -1
+        );
+    }
+
+    hasDataChildren(node: TreeInputNode) {
+        return (
+            node.children.length > 0 &&
+            node.children.find(c => c.dataNode) !== undefined
+        );
+    }
+
+    existsSelectedNode(node: TreeInputNode) {
+        return (
+            this.staticProperty.selectedNodesInternalNames.find(
+                nodeName => nodeName === node.internalNodeName,
+            ) !== undefined
+        );
+    }
+}
diff --git 
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-service.service.ts
 
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-service.service.ts
new file mode 100644
index 0000000000..08944f9416
--- /dev/null
+++ 
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-service.service.ts
@@ -0,0 +1,18 @@
+import { Injectable } from '@angular/core';
+import { RuntimeResolvableTreeInputStaticProperty } from 
'@streampipes/platform-services';
+
+@Injectable({
+    providedIn: 'root',
+})
+export class StaticTreeInputServiceService {
+    constructor() {}
+
+    getSelectedNodeIndex(
+        staticProperty: RuntimeResolvableTreeInputStaticProperty,
+        internalNodeName: string,
+    ) {
+        return staticProperty.selectedNodesInternalNames.indexOf(
+            internalNodeName,
+        );
+    }
+}

Reply via email to