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"
- > {{ 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"> {{ 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,
+ );
+ }
+}