You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by ri...@apache.org on 2022/02/14 17:41:17 UTC

[incubator-streampipes] branch experimental-module-federation-494 updated: [STREAMPIPES-510] Move pipeline editor selection panel to the left

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

riemer pushed a commit to branch experimental-module-federation-494
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git


The following commit(s) were added to refs/heads/experimental-module-federation-494 by this push:
     new 705a121  [STREAMPIPES-510] Move pipeline editor selection panel to the left
705a121 is described below

commit 705a121adcd03f302931726d0dfd7ec43dfe7b5c
Author: Dominik Riemer <do...@gmail.com>
AuthorDate: Mon Feb 14 18:40:50 2022 +0100

    [STREAMPIPES-510] Move pipeline editor selection panel to the left
---
 .../pipeline-assembly.component.html               | 101 ++++---
 .../pipeline-assembly.component.scss               |  23 +-
 .../pipeline-assembly.component.ts                 |   8 +-
 .../pipeline-element-icon-stand-row.component.html |  58 ++++
 .../pipeline-element-icon-stand-row.component.scss |  83 ++++++
 .../pipeline-element-icon-stand-row.component.ts   |  61 ++++
 .../pipeline-element-icon-stand.component.html     | 129 +++++---
 .../pipeline-element-icon-stand.component.scss     |  94 +++++-
 .../pipeline-element-icon-stand.component.ts       | 264 ++++++++---------
 .../pipeline-element.component.css                 |   6 +-
 .../pipeline-element.component.html                |   2 +-
 .../pipeline-element/pipeline-element.component.ts |   2 +-
 .../components/pipeline/pipeline.component.css     |   2 +-
 .../components/pipeline/pipeline.component.html    |   4 +-
 .../components/pipeline/pipeline.component.ts      |  10 +-
 ui/src/app/editor/editor.component.html            |  46 +--
 ui/src/app/editor/editor.component.scss            |   4 -
 ui/src/app/editor/editor.component.ts              | 329 ++++++++-------------
 ui/src/app/editor/editor.module.ts                 |   9 +
 ui/src/app/editor/model/editor.model.ts            |  13 +-
 ui/src/app/editor/services/editor.service.ts       |  21 +-
 .../pipeline-element-group-filter.pipe.ts}         |  30 +-
 .../pipeline-element-name-filter.pipe.ts}          |  31 +-
 .../pipeline-element-type-filter.pipe.ts}          |  30 +-
 .../app/services/get-element-icon-text.service.ts  |   4 +-
 ui/src/scss/sp/colors.scss                         |   1 +
 ui/src/scss/sp/layout.scss                         |   2 +-
 ui/src/scss/sp/main.scss                           |   7 +
 ui/src/scss/sp/pipeline-element-options.scss       |   7 +-
 29 files changed, 798 insertions(+), 583 deletions(-)

diff --git a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.html b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.html
index 9101a37..4e39a96 100644
--- a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.html
+++ b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.html
@@ -17,9 +17,12 @@
   -->
 
 <div fxFlex="100" fxLayout="column">
-    <div class="pipeline-assembly-options sp-blue-bg">
+    <div class="pipeline-assembly-options sp-bg-lightgray page-container-nav">
         <div fxFlex="100" fxLayout="row" fxLayoutAlign="start center">
-            <button mat-button matTooltip="Save Pipeline" [matTooltipPosition]="'above'"
+            <button mat-button
+                    mat-raised-button
+                    color="accent"
+                    matTooltip="Save Pipeline" [matTooltipPosition]="'above'"
                     [disabled]="!pipelineValidationService.pipelineValid"
                     (click)="submit()" type="submit"
                     data-cy="sp-editor-save-pipeline">
@@ -30,17 +33,20 @@
             </button>
             <span class="assembly-options-divider"></span>
             <!-- TODO: Use this once copying of elements is supported -->
-<!--            <button mat-button mat-icon-button matTooltip="Pan" [matTooltipPosition]="'above'"-->
-<!--                    [disabled]="!selectMode"-->
-<!--                    (click)="toggleSelectMode()">-->
-<!--                <i class="material-icons">open_with</i>-->
-<!--            </button>-->
-<!--            <button mat-button mat-icon-button matTooltip="Select" [matTooltipPosition]="'above'"-->
-<!--                    [disabled]="selectMode"-->
-<!--                    (click)="toggleSelectMode()">-->
-<!--                <i class="material-icons">mode_edit</i>-->
-<!--            </button>-->
-            <button mat-button matTooltip="Data Preview" [matTooltipPosition]="'above'"
+            <!--            <button mat-button mat-icon-button matTooltip="Pan" [matTooltipPosition]="'above'"-->
+            <!--                    [disabled]="!selectMode"-->
+            <!--                    (click)="toggleSelectMode()">-->
+            <!--                <i class="material-icons">open_with</i>-->
+            <!--            </button>-->
+            <!--            <button mat-button mat-icon-button matTooltip="Select" [matTooltipPosition]="'above'"-->
+            <!--                    [disabled]="selectMode"-->
+            <!--                    (click)="toggleSelectMode()">-->
+            <!--                <i class="material-icons">mode_edit</i>-->
+            <!--            </button>-->
+            <button mat-button
+                    color="accent"
+                    matTooltip="Data Preview"
+                    [matTooltipPosition]="'above'"
                     (click)="triggerPipelinePreview()" [disabled]="isPipelineAssemblyEmpty()">
                 <div fxLayoutAlign="start center" fxLayout="row">
                     <i class="material-icons">visibility</i>
@@ -49,39 +55,35 @@
                 </div>
             </button>
             <span class="assembly-options-divider"></span>
-            <button mat-button mat-icon-button matTooltip="Auto Layout" [matTooltipPosition]="'above'"
+            <button mat-button
+                    color="accent"
+                    mat-icon-button
+                    matTooltip="Auto Layout" [matTooltipPosition]="'above'"
                     (click)="autoLayout()">
                 <i class="material-icons">settings_overscan</i>
             </button>
-            <button mat-button mat-icon-button matTooltip="Add pipeline element" [matTooltipPosition]="'above'"
+            <button mat-button
+                    color="accent"
+                    mat-icon-button
+                    matTooltip="Add pipeline element" [matTooltipPosition]="'above'"
                     (click)="openDiscoverDialog()"
                     data-cy="sp-editor-add-pipeline-element">
                 <i class="material-icons">add</i>
             </button>
             <div fxLayout="column" fxLayoutAlign="start center" class="pipeline-cache-block">
-                <div fxFlex="100" fxLayoutAlign="start center" fxLayout="row" *ngIf="pipelineCached || pipelineCacheRunning">
+                <div fxFlex="100" fxLayoutAlign="start center" fxLayout="row"
+                     *ngIf="pipelineCached || pipelineCacheRunning">
                     <div *ngIf="pipelineCached" fxLayout="row" fxLayoutAlign="start center">
-                        <mat-spinner [mode]="'indeterminate'" class="mat-spinner-color" [diameter]="15" *ngIf="pipelineCacheRunning"></mat-spinner>
+                        <mat-spinner [mode]="'indeterminate'" class="mat-spinner-color" [diameter]="15"
+                                     *ngIf="pipelineCacheRunning"></mat-spinner>
                         <span>&nbsp;{{pipelineCacheRunning ? '&nbsp;Saving pipeline modifications' : 'All pipeline modifications saved.'}}</span>
                     </div>
                 </div>
             </div>
             <span fxFlex></span>
-            <div style="position:relative;">
-                <sp-error-hint
-                    [displayMessages]="!isPipelineAssemblyEmpty()"
-                    [errorMessages]="pipelineValidationService.errorMessages"
-                    [validationString]="'Pipeline'">
-                </sp-error-hint>
-            </div>
-            <button mat-button mat-icon-button
-                    [matTooltip]="pipelineCanvasMaximized ? 'Minimize' : 'Maximize'"
-                    [matTooltipPosition]="'above'"
-                    (click)="toggleCanvasMaximized()">
-                <i class="material-icons" *ngIf="!pipelineCanvasMaximized">open_in_full</i>
-                <i class="material-icons" *ngIf="pipelineCanvasMaximized">close_fullscreen</i>
-            </button>
-            <button mat-button mat-icon-button matTooltip="Clear Assembly Area"
+            <button mat-button
+                    color="accent"
+                    mat-icon-button matTooltip="Clear Assembly Area"
                     [matTooltipPosition]="'above'"
                     [disabled]="editorService.pipelineAssemblyEmpty"
                     (click)="showClearAssemblyConfirmDialog($event)">
@@ -89,8 +91,15 @@
             </button>
         </div>
     </div>
-    <div id="outerAssemblyArea" class="outerAssembly sp-blue-border-nopadding">
+    <div id="outerAssemblyArea" class="outerAssembly assembly-border">
         <div class="pipeline-canvas-outer" #outerCanvas>
+            <div class="pipeline-validation-hint">
+                <sp-error-hint
+                        [displayMessages]="!isPipelineAssemblyEmpty()"
+                        [errorMessages]="pipelineValidationService.errorMessages"
+                        [validationString]="'Pipeline'">
+                </sp-error-hint>
+            </div>
             <div class="pan-control">
                 <div class="pan-zoom-control-buttons">
                     <div class="pan-zoom-button pan-left" (click)="panLeft()">
@@ -121,18 +130,20 @@
                     </div>
                 </div>
             </div>
-                <pipeline class="canvas jtk-surface" id="assembly" #pipelineComponent
-                          [pipelineValid]="pipelineValid"
-                          [canvasId]="'assembly'"
-                          [rawPipelineModel]="rawPipelineModel"
-                          [allElements]="allElements"
-                          [preview]="false"
-                          [pipelineCached]="pipelineCached"
-                          [pipelineCanvasMetadata]="pipelineCanvasMetadata"
-                          [pipelineCacheRunning]="pipelineCacheRunning"
-                          (pipelineCachedChanged)="pipelineCached=$event"
-                          (pipelineCacheRunningChanged)="pipelineCacheRunning=$event" style="position:absolute;">
-                </pipeline>
+            <pipeline class="canvas jtk-surface" id="assembly"
+                      #pipelineComponent
+                      [pipelineValid]="pipelineValid"
+                      [canvasId]="'assembly'"
+                      [rawPipelineModel]="rawPipelineModel"
+                      [allElements]="allElements"
+                      [preview]="false"
+                      [pipelineCached]="pipelineCached"
+                      [pipelineCanvasMetadata]="pipelineCanvasMetadata"
+                      [pipelineCacheRunning]="pipelineCacheRunning"
+                      (pipelineCachedChanged)="pipelineCached=$event"
+                      (pipelineCacheRunningChanged)="pipelineCacheRunning=$event" style="position:absolute;">
+            </pipeline>
+
         </div>
     </div>
 </div>
diff --git a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss
index 9337454..52b217d 100644
--- a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss
+++ b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss
@@ -19,7 +19,7 @@
 @import '../../../../scss/_variables.scss';
 
 .mat-spinner-color::ng-deep svg circle{
-    stroke: #FFFFFF !important;
+    stroke: var(--color-accent) !important;
 }
 
 .pipeline-cache-progress {
@@ -66,10 +66,14 @@
 
 .pipeline-assembly-options {
     padding-left: 5px;
-    color: white;
+    border: 1px solid var(--color-bg-3);
     //background: #f6f6f6;
 }
 
+.assembly-border {
+    border: 1px solid var(--color-bg-3);
+}
+
 .jtk-surface-nopan {
     overflow: scroll !important;
     cursor:default;
@@ -88,6 +92,15 @@
 
 }
 
+.pipeline-validation-hint {
+    position: absolute;
+    right: 10px;
+    top: 20px;
+    z-index: 10;
+    background: var(--color-bg-1);
+    box-shadow: 0.175em 0.175em 0 0 rgba(15, 28, 63, 0.125);
+}
+
 .pan-control {
     position: absolute;
     right: 60px;
@@ -151,13 +164,15 @@
 }
 
 .assembly-options-divider {
-    width: 2px;
+    width: 1px;
     height: 70%;
     margin-left: 10px;
     margin-right: 10px;
-    background: #b8b8b8;
+    background: var(--color-bg-3);
 }
 
 .color-warn {
     color: var(--color-warn);
 }
+
+
diff --git a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.ts b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.ts
index fd23987..7f3f189 100644
--- a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.ts
+++ b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.ts
@@ -40,7 +40,7 @@ import { DialogService } from '../../../core-ui/dialog/base-dialog/base-dialog.s
 import { ConfirmDialogComponent } from '../../../core-ui/dialog/confirm-dialog/confirm-dialog.component';
 import { MatDialog } from '@angular/material/dialog';
 import { EditorService } from '../../services/editor.service';
-import { PipelineService, PipelineCanvasMetadataService, PipelineCanvasMetadata } from '@streampipes/platform-services';
+import { PipelineCanvasMetadata, PipelineCanvasMetadataService, PipelineService } from '@streampipes/platform-services';
 import { JsplumbFactoryService } from '../../services/jsplumb-factory.service';
 import Panzoom, { PanzoomObject } from '@panzoom/panzoom';
 import { PipelineElementDraggedService } from '../../services/pipeline-element-dragged.service';
@@ -290,11 +290,6 @@ export class PipelineAssemblyComponent implements OnInit, AfterViewInit {
         return this.rawPipelineModel.length === 0 || this.rawPipelineModel.every(pe => pe.settings.disabled);
     }
 
-    toggleCanvasMaximized() {
-        this.pipelineCanvasMaximized = !(this.pipelineCanvasMaximized);
-        this.pipelineCanvasMaximizedEmitter.emit(this.pipelineCanvasMaximized);
-    }
-
     panLeft() {
         this.pan(100, 0);
     }
@@ -341,5 +336,4 @@ export class PipelineAssemblyComponent implements OnInit, AfterViewInit {
             }
         });
     }
-
 }
diff --git a/ui/src/app/editor/components/pipeline-element-icon-stand-row/pipeline-element-icon-stand-row.component.html b/ui/src/app/editor/components/pipeline-element-icon-stand-row/pipeline-element-icon-stand-row.component.html
new file mode 100644
index 0000000..25eb72d
--- /dev/null
+++ b/ui/src/app/editor/components/pipeline-element-icon-stand-row/pipeline-element-icon-stand-row.component.html
@@ -0,0 +1,58 @@
+<!--
+  ~ 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 fxLayout="row"
+     class="draggable-pipeline-element"
+     fxFlex="100"
+     id="{{ element.appId }}"
+     [attr.data-pe]="element.elementId"
+     (mouseenter)="updateMouseOver(element.name)"
+     (mouseleave)="updateMouseOver('')">
+    <div fxFlex="50px">
+        <div matTooltip="{{element.name}}" [matTooltipPosition]="'above'"
+             class="draggable-icon-editor tt"
+             [ngClass]="activeCssClass">
+            <span id="container" class="pe-container">
+            <pipeline-element
+                    id="pe-icon-stand-{{ element.appId }}"
+                    style="margin-left:-3%"
+                    [iconStandSize]="true"
+                    [attr.data-cy]="'sp-pipeline-element-' + cypressName"
+                    [pipelineElement]="element"
+                    [preview]="false"></pipeline-element>
+            </span>
+        </div>
+    </div>
+    <div fxFlex fxLayoutAlign="start start" fxLayout="column">
+        <div class="element-name" fxLayoutAlign="start start">
+            {{element.name}}
+        </div>
+        <div class="element-description" *ngIf="currentMouseOver" fxLayoutAlign="start start">
+            <small>{{element.description}}</small>
+        </div>
+    </div>
+    <div fxLayoutAlign="end start" *ngIf="currentMouseOver">
+        <button mat-button
+                mat-raised-button
+                mat-icon-button
+                color="accent"
+                class="help-button-stand"
+                (click)="openHelpDialog(element)">?
+        </button>
+    </div>
+</div>
+
diff --git a/ui/src/app/editor/components/pipeline-element-icon-stand-row/pipeline-element-icon-stand-row.component.scss b/ui/src/app/editor/components/pipeline-element-icon-stand-row/pipeline-element-icon-stand-row.component.scss
new file mode 100644
index 0000000..085ba50
--- /dev/null
+++ b/ui/src/app/editor/components/pipeline-element-icon-stand-row/pipeline-element-icon-stand-row.component.scss
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ *
+ */
+
+.draggable-icon-editor {
+  cursor: move;
+  vertical-align: top;
+  background-color: var(--color-pe);
+  display: inline-block;
+  height: 35px;
+  width: 35px;
+  line-height: 35px;
+  box-shadow: 1px 1px 2px var(--color-shadow);
+  text-align: center;
+  z-index:100;
+}
+
+.draggable-icon-drag {
+  cursor: pointer;
+  vertical-align: top;
+  background: var(--color-pe);
+  display: inline-block;
+  height: 70px;
+  width: 70px;
+  line-height: 70px;
+  box-shadow: 1px 1px 2px var(--color-shadow);
+  text-align: center;
+  background: white;
+  z-index: 100;
+}
+
+.draggable-pipeline-element {
+  cursor: move;
+  z-index: 100;
+}
+
+.element-name {
+  font-size: 11pt;
+  font-weight: 500;
+}
+
+.element-description {
+  font-weight: 300;
+}
+
+.help-button-stand {
+  padding: 0;
+  min-width: 0;
+  width: 20px;
+  height: 20px;
+  flex-shrink: 0;
+  line-height: 20px;
+  border-radius: 0;
+  margin-right: 5px;
+}
+
+.pe-container {
+  position:relative;
+  display:block;
+  width:35px;
+  height:35px;
+}
+
+.pe-container-drag {
+  position:relative;
+  display:block;
+  width:70px;
+  height:70px;
+}
diff --git a/ui/src/app/editor/components/pipeline-element-icon-stand-row/pipeline-element-icon-stand-row.component.ts b/ui/src/app/editor/components/pipeline-element-icon-stand-row/pipeline-element-icon-stand-row.component.ts
new file mode 100644
index 0000000..bd799a0
--- /dev/null
+++ b/ui/src/app/editor/components/pipeline-element-icon-stand-row/pipeline-element-icon-stand-row.component.ts
@@ -0,0 +1,61 @@
+/*
+ * 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, Input, OnInit, } from '@angular/core';
+import { PipelineElementType, PipelineElementUnion } from '../../model/editor.model';
+import { PipelineElementTypeUtils } from '../../utils/editor.utils';
+import { EditorService } from '../../services/editor.service';
+
+
+@Component({
+  selector: 'sp-pe-icon-stand-row',
+  templateUrl: './pipeline-element-icon-stand-row.component.html',
+  styleUrls: ['./pipeline-element-icon-stand-row.component.scss']
+})
+export class PipelineElementIconStandRowComponent implements OnInit {
+
+  @Input()
+  element: PipelineElementUnion;
+
+  activeCssClass: string;
+  cypressName: string;
+
+  currentMouseOver = false;
+
+  constructor(private editorService: EditorService) {
+
+  }
+
+  ngOnInit(): void {
+    const activeType = PipelineElementTypeUtils.fromClassName(this.element['@class']);
+    this.activeCssClass = this.makeActiveCssClass(activeType);
+    this.cypressName = this.element.name.toLowerCase().replace(' ', '_');
+  }
+
+  makeActiveCssClass(elementType: PipelineElementType): string {
+    return PipelineElementTypeUtils.toCssShortHand(elementType);
+  }
+
+  updateMouseOver(e: string) {
+    this.currentMouseOver = !this.currentMouseOver;
+  }
+
+  openHelpDialog(pipelineElement) {
+    this.editorService.openHelpDialog(pipelineElement);
+  }
+}
diff --git a/ui/src/app/editor/components/pipeline-element-icon-stand/pipeline-element-icon-stand.component.html b/ui/src/app/editor/components/pipeline-element-icon-stand/pipeline-element-icon-stand.component.html
index e53dfee..21b8332 100644
--- a/ui/src/app/editor/components/pipeline-element-icon-stand/pipeline-element-icon-stand.component.html
+++ b/ui/src/app/editor/components/pipeline-element-icon-stand/pipeline-element-icon-stand.component.html
@@ -16,60 +16,97 @@
   ~
   -->
 <div fxFlex="100" fxLayout="column" class="border">
-    <div class="editorIconStandOptions" style="padding:0px;">
-        <div fxFlex="100" fxLayout="row" fxLayoutAlign="start start" style="padding-top:2px;padding-left:10px;">
-
+    <div class="editorIconStandOptions sp-bg-lightgray page-container-nav" style="padding:0px;">
+        <div fxFlex="100" fxLayout="row" fxLayoutAlign="start center" style="padding-left:5px;">
             <div fxLayoutAlign="start center" fxLayout="row">
-                <div fxLayout="row" style="margin-right: 5px">
+                <div fxLayout="row" fxLayoutAlign="start center">
                     <i class="material-icons sp-accent">search</i>
-                    <input type="text" (keyup)="applyFilter()" [(ngModel)]="elementFilter" placeholder="Find Element"
+                    <input type="text" (keyup)="makeDraggable()" [(ngModel)]="elementFilter" placeholder="Find Element"
                            style="border: none;border-bottom: solid 2px rgb(27, 20, 100);">&nbsp;
+                    <button mat-button
+                            mat-icon-button
+                            color="accent"
+                            (click)="startCreatePipelineTour()"
+                            [matTooltip]="'Tutorial'" style="margin-right: 5px;">
+                        <i class="material-icons">
+                            school
+                        </i>
+                    </button>
                 </div>
-                <div fxLayout="row">
-                    <button mat-button [matMenuTriggerFor]="menu"><div class="menu-text-color"><i class="material-icons">list</i>&nbsp;Filter by category</div></button>
-                    <mat-menu #menu="matMenu">
-                        <button mat-menu-item (click)="selectAllOptions()">
-                            <div class="menu-text-color"><i class="material-icons">done</i>&nbsp;Select all</div>
-                        </button>
-                        <button mat-menu-item (click)="deselectAllOptions()">
-                            <div class="menu-text-color"><i class="material-icons">clear</i>&nbsp;Deselect all</div>
-                        </button>
-                        <mat-divider></mat-divider>
-                        <button (click)="toggleOption(option)" mat-menu-item *ngFor="let option of currentCategories">
-                            <div class="menu-text-color" *ngIf="optionSelected(option)">
-                                <i class="material-icons">visibility</i>&nbsp;{{option.label}}
+            </div>
+        </div>
+    </div>
+    <div id="editor-icon-stand"
+         class="icon-stand"
+         *ngIf="allElements">
+        <div fxFlex="100"
+             fxLayout="column">
+            <div *ngFor="let availableType; let i = index of availableTypes"
+                 fxLayout="column"
+                 class="panel-outer">
+                <div class="panel-header"
+                     fxLayout="row"
+                     fxLayoutAlign="start center"
+                     [ngStyle]="{background: 'var(--color-bg-2)'}">
+                    <div class="panel-title"
+                         fxLayoutAlign="start center">
+                        {{availableType.title}}
+                    </div>
+                    <span fxFlex>
+                        </span>
+                    <button mat-icon-button
+                            (click)="toggleOpen(availableType)"
+                            *ngIf="!availableType.open">
+                        <mat-icon>expand_more</mat-icon>
+                    </button>
+                    <button mat-icon-button
+                            (click)="toggleOpen(availableType)"
+                            *ngIf="availableType.open">
+                        <mat-icon>expand_less</mat-icon>
+                    </button>
+                </div>
+                <div fxFlex="100"
+                     fxLayout="column"
+                     *ngIf="availableType.open" class="panel-content">
+                    <div fxLayout="row"
+                         fxLayoutAlign="end center"
+                         class="panel-options"
+                         *ngIf="!(availableType.filters[0] === 1)">
+                        <small>Sort:&nbsp;<a (click)="changeSorting(availableType, 'group')"
+                                             class="sort-option"
+                                             [ngClass]="availableType.sort === 'group' ? 'sort-selected' : 'sort-unselected'">Group</a>&nbsp;|&nbsp;
+                            <a (click)="changeSorting(availableType, 'name')"
+                               class="sort-option"
+                               [ngClass]="availableType.sort === 'name' ? 'sort-selected' : 'sort-unselected'">Name</a></small>
+                    </div>
+                    <div fxFlex="100"
+                         fxLayout="column"
+                         *ngIf="availableType.sort === 'name'">
+                        <div fxLayout="column"
+                             fxFlex="100"
+                             *ngFor="let element of allElements | pipelineElementType:availableType.filters | pipelineElementName:elementFilter"
+                             class="pe-row">
+                            <sp-pe-icon-stand-row [element]="element" fxFlex="100"></sp-pe-icon-stand-row>
+                        </div>
+                    </div>
+                    <div fxFlex="100"
+                         fxLayout="column"
+                         *ngIf="availableType.sort === 'group' && categoriesReady">
+                        <div *ngFor="let category of allCategories.get(availableType.filters[0])"
+                             fxLayout="column">
+                            <div class="group-outer">
+                                <span class="group-title">{{category.label}}</span>
                             </div>
-                            <div  class="menu-text-color" *ngIf="!optionSelected(option)">
-                                <i class="material-icons">visibility_off</i>&nbsp;{{option.label}}
+                            <div fxLayout="column"
+                                 fxFlex="100"
+                                 *ngFor="let element of allElements | pipelineElementType:availableType.filters | pipelineElementName:elementFilter | pipelineElementGroup:category"
+                                 class="pe-row">
+                                <sp-pe-icon-stand-row [element]="element" fxFlex="100"></sp-pe-icon-stand-row>
                             </div>
-                        </button>
-                    </mat-menu>
+                        </div>
+                    </div>
                 </div>
             </div>
         </div>
     </div>
-    <div flex id="editor-icon-stand" class="icon-stand" *ngIf="currentlyFilteredElements">
-        <span matTooltip="{{element.name}}" [matTooltipPosition]="'above'" [matTooltipClass]="tooltip" id="{{ element.appId }}" (mouseenter)="updateMouseOver(element.name)"
-              (mouseleave)="updateMouseOver('')"
-              *ngFor="let element of currentlyFilteredElements"
-              class="draggable-icon tt"
-              [attr.data-pe]="element.elementId"
-              [ngClass]="activeCssClass">
-            <span id="container" style="position:relative;display:block;width:80px;height:80px;">
-            <pipeline-element
-                    id="pe-icon-stand-{{ element.appId }}"
-                    style="margin-left:-3%" [iconStandSize]="true"
-                    [attr.data-cy]="'sp-pipeline-element-' + element.name.toLowerCase().replaceAll(' ', '_')"
-                    [pipelineElement]="element"
-                              [preview]="false"></pipeline-element>
-                <span style="display:block;width:100%;height:100%;position:absolute; top:0; left:0;"
-                      *ngIf="currentElementName===element.name">
-                    <span class="help-button-icon-stand" style="z-index:10"><button mat-button mat-icon-button
-                            (click)="openHelpDialog(element)"
-                            style="margin:0px;font-size:20px;">?</button>
-                    </span>
-                </span>
-            </span>
-        </span>
-    </div>
 </div>
diff --git a/ui/src/app/editor/components/pipeline-element-icon-stand/pipeline-element-icon-stand.component.scss b/ui/src/app/editor/components/pipeline-element-icon-stand/pipeline-element-icon-stand.component.scss
index d5a2288..ca19855 100644
--- a/ui/src/app/editor/components/pipeline-element-icon-stand/pipeline-element-icon-stand.component.scss
+++ b/ui/src/app/editor/components/pipeline-element-icon-stand/pipeline-element-icon-stand.component.scss
@@ -19,20 +19,94 @@
 @import '../../../../scss/_variables.scss';
 
 .menu-text-color {
-    color: var(--color-accent);
-    display: flex;
-    align-items: center;
+  color: var(--color-accent);
+  display: flex;
+  align-items: center;
 }
 
 .icon-stand {
-    max-height: 200px;
-    padding-bottom: 1%;
-    overflow-y: auto;
-    border-top: 0px;
-    background: var(--color-bg-1);
+  max-height: calc(100vh - 118px);
+  overflow-y: auto;
+  background: var(--color-bg-1);
+  margin-top: 5px;
 }
 
 .border {
-    border: 1px solid var(--color-bg-2);
-    border-top: 0;
+  border: 1px solid var(--color-bg-3);
+}
+
+.pe-row {
+  padding-top: 5px;
+  padding-bottom: 5px;
+  padding-left: 5px;
+}
+
+
+.pe-row:nth-child(even) {
+  background-color: var(--color-bg-1);
+}
+
+.pe-row:nth-child(odd) {
+  background-color: var(--color-bg-0);
+}
+
+.pe-row:hover {
+  background-color: var(--color-bg-2);
+  cursor: pointer;
+}
+
+.panel-header {
+  padding: 3px;
+}
+
+.panel-title {
+  font-weight: 400;
+  padding-left: 5px;
+}
+
+.panel-options {
+  margin-top: 10px;
+  margin-bottom: 5px;
+  margin-right: 5px;
+}
+
+.panel-content {
+  margin-top: 10px;
+}
+
+.sort-selected {
+  padding: 3px;
+  background: var(--color-accent);
+  color: white;
+}
+
+.sort-unselected {
+  padding: 3px;
+  color: var(--color-accent);
+}
+
+.panel-outer {
+  margin-bottom: 10px;
+  margin-left: 0;
+  margin-right: 0px;
+  border-right: 1px solid var(--color-bg-3);
+  border-bottom: 1px solid var(--color-bg-3);
+  border-top: 1px solid var(--color-bg-3);
+}
+
+.group-outer {
+  margin-top: 15px;
+  margin-bottom: 10px;
+}
+
+.group-title {
+  padding-left: 5px;
+  padding-right: 5px;
+  border-radius: 3px;
+  font-size: small;
+  background: var(--color-primary);
+}
+
+.sort-option {
+  cursor: pointer;
 }
diff --git a/ui/src/app/editor/components/pipeline-element-icon-stand/pipeline-element-icon-stand.component.ts b/ui/src/app/editor/components/pipeline-element-icon-stand/pipeline-element-icon-stand.component.ts
index 5abe418..3efdf09 100644
--- a/ui/src/app/editor/components/pipeline-element-icon-stand/pipeline-element-icon-stand.component.ts
+++ b/ui/src/app/editor/components/pipeline-element-icon-stand/pipeline-element-icon-stand.component.ts
@@ -16,162 +16,132 @@
  *
  */
 
-import { AfterViewInit, Component, Input, OnInit, } from '@angular/core';
+import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output, } from '@angular/core';
 import { RestApi } from '../../../services/rest-api.service';
-import {
-    PipelineElementIdentifier,
-    PipelineElementType,
-    PipelineElementUnion
-} from '../../model/editor.model';
-import { PipelineElementTypeUtils } from '../../utils/editor.utils';
+import { PeCategory, PipelineElementType, PipelineElementUnion } from '../../model/editor.model';
 import { EditorService } from '../../services/editor.service';
 import { zip } from 'rxjs';
 
 
 @Component({
-    selector: 'pipeline-element-icon-stand',
-    templateUrl: './pipeline-element-icon-stand.component.html',
-    styleUrls: ['./pipeline-element-icon-stand.component.scss']
+  selector: 'pipeline-element-icon-stand',
+  templateUrl: './pipeline-element-icon-stand.component.html',
+  styleUrls: ['./pipeline-element-icon-stand.component.scss']
 })
 export class PipelineElementIconStandComponent implements OnInit, AfterViewInit {
 
-
-    _currentElements: PipelineElementUnion[];
-
-    currentlyFilteredElements: PipelineElementUnion[];
-
-    elementFilter = '';
-    allCategories: any = [];
-    currentCategories: any = [];
-    selectedOptions: any = [];
-
-    _activeType: PipelineElementType;
-    activeCssClass: string;
-
-    currentElementName: string;
-
-    constructor(private restApi: RestApi,
-                private editorService: EditorService) {
-
-    }
-
-    ngOnInit(): void {
-        this.loadOptions();
-    }
-
-    ngAfterViewInit() {
-        this.makeDraggable();
-    }
-
-    openHelpDialog(pipelineElement) {
-        this.editorService.openHelpDialog(pipelineElement);
-    }
-
-    updateMouseOver(elementAppId: string) {
-        this.currentElementName = elementAppId;
-    }
-
-    loadOptions() {
-        zip(this.editorService.getEpCategories(),
-            this.editorService.getEpaCategories(),
-            this.editorService.getEcCategories()).subscribe((results) => {
-                this.allCategories[PipelineElementType.DataStream] = results[0];
-                this.allCategories[PipelineElementType.DataSet] = results[0];
-                this.allCategories[PipelineElementType.DataProcessor] = results[1];
-                this.allCategories[PipelineElementType.DataSink] = results[2];
-                this.currentCategories = this.allCategories[0];
-                this.selectedOptions = [...this.currentCategories];
-            });
-    }
-
-    applyFilter() {
-        this.currentlyFilteredElements = this.currentElements.filter(el => {
-            return this.matchesText(el) && this.matchesCategory(el);
-        });
-        this.makeDraggable();
-    }
-
-    matchesText(el: PipelineElementUnion): boolean {
-        return this.elementFilter === '' || el.name.toLowerCase().includes(this.elementFilter.toLowerCase());
-    }
-
-    matchesCategory(el: PipelineElementUnion): boolean {
-        return this._activeType === PipelineElementType.DataStream ||
-            this._activeType === PipelineElementType.DataSet ?
-            this.selectedOptions.some(c => el.category.some(cg => c.type === cg)) :
-            this.selectedOptions.some(c => !(el.category) || el.category.length === 0 || el.category.some(cg => c.code === cg));
-    }
-
-    toggleOption(option) {
-        if (this.optionSelected(option)) {
-            this.selectedOptions.splice(option, 1);
-        } else {
-            this.selectedOptions.push(option);
-        }
-        this.applyFilter();
-    }
-
-    optionSelected(option) {
-        return this._activeType === PipelineElementType.DataStream ||
-        this._activeType === PipelineElementType.DataSet ?
-            this.selectedOptions.map(o => o.type).indexOf(option.type) > -1 :
-            this.selectedOptions.map(o => o.code).indexOf(option.code) > -1;
-    }
-
-    selectAllOptions() {
-        this.selectedOptions = [...this.currentCategories];
-        this.applyFilter();
-    }
-
-    deselectAllOptions() {
-        this.selectedOptions = [];
-        this.applyFilter();
-    }
-
-    @Input()
-    set activeType(value: PipelineElementIdentifier) {
-        const activeType = PipelineElementTypeUtils.fromClassName(value);
-        this._activeType = activeType;
-        if (this.allCategories.length > 0) {
-            this.currentCategories = this.allCategories[this._activeType];
-            this.selectedOptions = [...this.currentCategories];
+  availableTypes = [
+    {
+      title: 'Data Sources',
+      filters: [PipelineElementType.DataStream, PipelineElementType.DataSet],
+      open: true,
+      color: 'var(--color-stream)',
+      sort: 'name'
+    },
+    {
+      title: 'Data Processors',
+      filters: [PipelineElementType.DataProcessor],
+      open: true,
+      color: 'var(--color-processor)',
+      sort: 'name'
+    },
+    {
+      title: 'Data Sinks',
+      filters: [PipelineElementType.DataSink],
+      open: true,
+      color: 'var(--color-sink)',
+      sort: 'name'
+    }];
+
+  @Input()
+  allElements: PipelineElementUnion[];
+
+  @Output()
+  startTourEmitter: EventEmitter<void> = new EventEmitter<void>();
+
+  elementFilter = '';
+  allCategories: Map<PipelineElementType, PeCategory[]> = new Map();
+  categoriesReady = false;
+  uncategorized: PeCategory = {code: 'UNCATEGORIZED', label: 'Uncategorized', description: ''};
+
+  constructor(private restApi: RestApi,
+              private editorService: EditorService) {
+
+  }
+
+  ngOnInit(): void {
+    this.loadOptions();
+  }
+
+  ngAfterViewInit() {
+    this.makeDraggable();
+  }
+
+  loadOptions() {
+    zip(this.editorService.getEpCategories(),
+      this.editorService.getEpaCategories(),
+      this.editorService.getEcCategories()).subscribe((results) => {
+      results[0] = this.sort(results[0]).filter(category => this.filterForExistingCategories(category));
+      results[1] = this.sort(results[1]).filter(category => this.filterForExistingCategories(category));
+      results[2] = this.sort(results[2]).filter(category => this.filterForExistingCategories(category));
+      this.allCategories.set(PipelineElementType.DataStream, results[0]);
+      this.allCategories.set(PipelineElementType.DataProcessor, results[1]);
+      this.allCategories.set(PipelineElementType.DataSink, results[2]);
+      this.categoriesReady = true;
+    });
+  }
+
+  filterForExistingCategories(category: PeCategory): boolean {
+    return this.allElements
+      .filter(element => element.category)
+      .find(element => element.category.find(elCat => elCat === category.code)) !== undefined ||
+      ((category.code === this.uncategorized.code) && this.allElements.find(element => (!element.category)) !== undefined);
+  }
+
+  sort(categories: PeCategory[]) {
+    return categories.sort((a, b) => {
+      return a.label.localeCompare(b.label);
+    });
+  }
+
+  makeDraggable() {
+    setTimeout(() => {
+      ($('.draggable-pipeline-element') as any).draggable({
+        revert: 'invalid',
+        helper: ((ev) => {
+          const draggable = $(ev.currentTarget).find('.draggable-icon-editor').first().clone();
+          const draggableContainer = $(draggable).find('.pe-container').first();
+          $(draggable).removeClass('draggable-icon-editor');
+          $(draggable).addClass('draggable-icon-drag');
+          $(draggableContainer).removeClass('pe-container');
+          $(draggableContainer).addClass('pe-container-drag');
+          return draggable.clone();
+        }),
+        stack: '.draggable-pipeline-element',
+        start(el, ui) {
+          ui.helper.appendTo('#content');
+          $('#outerAssemblyArea').css('border', '3px dashed #39b54a');
+        },
+        stop(el, ui) {
+          $('#outerAssemblyArea').css('border', '1px solid var(--color-bg-3)');
         }
-        this.activeCssClass = this.makeActiveCssClass(activeType);
-        setTimeout(() => {
-            this.makeDraggable();
-        });
-    }
-
-    @Input()
-    set currentElements(value: PipelineElementUnion[]) {
-        this._currentElements = value;
-        this.elementFilter = '';
-        this.currentlyFilteredElements = this._currentElements;
-    }
-
-    get currentElements() {
-        return this._currentElements;
-    }
-
-    makeActiveCssClass(elementType: PipelineElementType): string {
-        return PipelineElementTypeUtils.toCssShortHand(elementType);
-    }
-
-    makeDraggable() {
-       setTimeout(() => {
-           ($('.draggable-icon') as any).draggable({
-               revert: 'invalid',
-               helper: 'clone',
-               stack: '.draggable-icon',
-               start (el, ui) {
-                   ui.helper.appendTo('#content');
-                   $('#outerAssemblyArea').css('border', '3px dashed #39b54a');
-               },
-               stop (el, ui) {
-                   $('#outerAssemblyArea').css('border', '3px solid rgb(156, 156, 156)');
-               }
-           });
-       });
-    }
+      });
+    });
+  }
+
+  toggleOpen(availableType: any): void {
+    availableType.open = !availableType.open;
+    this.makeDraggable();
+  }
+
+  startCreatePipelineTour() {
+    this.startTourEmitter.emit();
+  }
+
+  changeSorting(availableType: any,
+                sortMode: string) {
+    availableType.sort = sortMode;
+    this.makeDraggable();
+  }
 
 }
diff --git a/ui/src/app/editor/components/pipeline-element/pipeline-element.component.css b/ui/src/app/editor/components/pipeline-element/pipeline-element.component.css
index 41ecef0..85b4348 100644
--- a/ui/src/app/editor/components/pipeline-element/pipeline-element.component.css
+++ b/ui/src/app/editor/components/pipeline-element/pipeline-element.component.css
@@ -14,4 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
- */
\ No newline at end of file
+ */
+
+.text-small {
+    font-size: 8pt;
+}
diff --git a/ui/src/app/editor/components/pipeline-element/pipeline-element.component.html b/ui/src/app/editor/components/pipeline-element/pipeline-element.component.html
index 2a758a9..60a30dc 100644
--- a/ui/src/app/editor/components/pipeline-element/pipeline-element.component.html
+++ b/ui/src/app/editor/components/pipeline-element/pipeline-element.component.html
@@ -17,6 +17,6 @@
   -->
 
 <img [src]="image" *ngIf="showImage" style="{{iconSizeCss()}}">
-<span *ngIf="!showImage" class="element-text-icon">
+<span *ngIf="!showImage" [ngClass]="iconStandSize ? 'text-small<' : 'element-text-icon'">
             {{iconText}}
 </span>
diff --git a/ui/src/app/editor/components/pipeline-element/pipeline-element.component.ts b/ui/src/app/editor/components/pipeline-element/pipeline-element.component.ts
index 84fc539..5b28e74 100644
--- a/ui/src/app/editor/components/pipeline-element/pipeline-element.component.ts
+++ b/ui/src/app/editor/components/pipeline-element/pipeline-element.component.ts
@@ -78,7 +78,7 @@ export class PipelineElementComponent implements OnInit {
         } else if (this.preview) {
             return 'width:50px;height:50px;';
         } else if (this.iconStandSize) {
-            return 'width:50px;height:50px;margin-top:-5px;';
+            return 'width:30px;height:30px;';
         } else {
             return 'width:70px;height:70px;';
         }
diff --git a/ui/src/app/editor/components/pipeline/pipeline.component.css b/ui/src/app/editor/components/pipeline/pipeline.component.css
index 41ecef0..13cbc4a 100644
--- a/ui/src/app/editor/components/pipeline/pipeline.component.css
+++ b/ui/src/app/editor/components/pipeline/pipeline.component.css
@@ -14,4 +14,4 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
- */
\ No newline at end of file
+ */
diff --git a/ui/src/app/editor/components/pipeline/pipeline.component.html b/ui/src/app/editor/components/pipeline/pipeline.component.html
index 768997a..5a2def9 100644
--- a/ui/src/app/editor/components/pipeline/pipeline.component.html
+++ b/ui/src/app/editor/components/pipeline/pipeline.component.html
@@ -15,7 +15,6 @@
   ~ limitations under the License.
   ~
   -->
-
 <div [attr.data-jtk-managed]="pipelineElement.payload.dom"
      id="{{pipelineElement.payload.dom}}"
      style="{{getElementCss(pipelineElement.settings)}}"
@@ -44,7 +43,7 @@
         </div>
         <pipeline-element [pipelineElement]="pipelineElement.payload" [preview]="preview"></pipeline-element>
     </div>
-    <pipeline-element-options *ngIf="!preview && currentMouseOverElement==pipelineElement.payload.dom"
+    <pipeline-element-options *ngIf="!preview"
                               (delete)="handleDeleteOption($event)"
                               (customize)="showCustomizeDialog($event)"
                               [currentMouseOverElement]="currentMouseOverElement"
@@ -63,4 +62,3 @@
 
     </pipeline-element-preview>
 </div>
-
diff --git a/ui/src/app/editor/components/pipeline/pipeline.component.ts b/ui/src/app/editor/components/pipeline/pipeline.component.ts
index 4508749..e7c9b0b 100644
--- a/ui/src/app/editor/components/pipeline/pipeline.component.ts
+++ b/ui/src/app/editor/components/pipeline/pipeline.component.ts
@@ -200,7 +200,7 @@ export class PipelineComponent implements OnInit, OnDestroy {
       drop: (element, ui) => {
         const pipelineElementId = ui.draggable.data('pe');
         const pipelineElement: PipelineElementUnion = this.findPipelineElementByElementId(pipelineElementId);
-        if (ui.draggable.hasClass('draggable-icon')) {
+        if (ui.draggable.hasClass('draggable-pipeline-element')) {
           this.editorService.makePipelineAssemblyEmpty(false);
           const newElementId = pipelineElement.elementId + ':' + this.jsplumbService.makeId(5);
           const pipelineElementConfig = this.jsplumbService.createNewPipelineElementConfig(pipelineElement,
@@ -213,7 +213,7 @@ export class PipelineComponent implements OnInit, OnDestroy {
             this.showMixedStreamAlert();
           } else {
             this.rawPipelineModel.push(pipelineElementConfig);
-            if (ui.draggable.hasClass('set')) {
+            if (pipelineElementConfig.type === 'set') {
               setTimeout(() => {
                 this.editorService.updateDataSet(pipelineElementConfig.payload).subscribe(data => {
                   (pipelineElementConfig.payload as SpDataSet).eventGrounding = data.eventGrounding;
@@ -225,9 +225,9 @@ export class PipelineComponent implements OnInit, OnDestroy {
                     false);
                 });
               }, 0);
-            } else if (ui.draggable.hasClass('stream')) {
+            } else if (pipelineElementConfig.type === 'stream') {
               this.checkTopicModel(pipelineElementConfig);
-            } else if (ui.draggable.hasClass('sepa')) {
+            } else if (pipelineElementConfig.type === 'sepa') {
               setTimeout(() => {
                 this.jsplumbService.dataProcessorDropped(
                   pipelineElementConfig.payload.dom,
@@ -236,7 +236,7 @@ export class PipelineComponent implements OnInit, OnDestroy {
                   false
                 );
               }, 10);
-            } else if (ui.draggable.hasClass('action')) {
+            } else if (pipelineElementConfig.type === 'action') {
               setTimeout(() => {
                 this.jsplumbService.dataSinkDropped(
                   pipelineElementConfig.payload.dom,
diff --git a/ui/src/app/editor/editor.component.html b/ui/src/app/editor/editor.component.html
index 3ec9970..0a25281 100644
--- a/ui/src/app/editor/editor.component.html
+++ b/ui/src/app/editor/editor.component.html
@@ -17,43 +17,25 @@
   -->
 
 <div fxLayout="column" class="page-container">
-    <div fxLayout="row" class="border sp-tab-bg editor-canvas-options" *ngIf="!pipelineCanvasMaximized">
-        <div fxFlex="100" fxLayout="row" class="page-container-nav">
-            <div fxFlex fxLayoutAlign="start center" [attr.id]="'peType'">
-                <mat-tab-group [selectedIndex]="selectedIndex"
-                               (selectedIndexChange)="selectPipelineElements($event)" color="accent">
-                    <mat-tab
-                            *ngFor="let tab of tabs"
-                            [attr.id]="tab.shorthand"
-                            label="{{tab.title}}"></mat-tab>
-                </mat-tab-group>
+    <div class="fixed-height" fxLayout="row" fxFlex="100">
+        <div fxFlex="250px">
+            <div fxFlex="100" fxLayout="column" fxLayoutAlign="center center" *ngIf="!allElementsLoaded">
+                <mat-spinner [diameter]="30" color="accent" mode="indeterminate"></mat-spinner>
+                <p>Loading pipeline elements...</p>
             </div>
-            <div fxFlex="30" fxLayout="row" fxLayoutAlign="end center" style="padding-left:10px;font-size:14px;line-height:24px;">
-                <button mat-button mat-icon-button (click)="startCreatePipelineTour()"
-                        [matTooltip]="'Tutorial'" style="margin-right: 5px;">
-                    <i class="material-icons text-color">
-                        school
-                    </i>
-                </button>
+            <div id="shepherd-test"
+                 style="padding:0px;border-bottom:1px solid #ffffff;margin-right: 5px;">
+                <pipeline-element-icon-stand
+                        [allElements]="allElements"
+                        *ngIf="allElementsLoaded"
+                        (startTourEmitter)="startCreatePipelineTour()">
+                </pipeline-element-icon-stand>
             </div>
         </div>
-    </div>
-
-    <div class="fixed-height" fxLayout="column" fxFlex="100">
-        <div id="shepherd-test"
-             style="padding:0px;border-bottom:1px solid #ffffff">
-            <pipeline-element-icon-stand
-                    [activeType]="activeType"
-                    [currentElements]="currentElements"
-                    *ngIf="allElementsLoaded && !pipelineCanvasMaximized">
-            </pipeline-element-icon-stand>
-        </div>
-        <pipeline-assembly fxFlex="1"
-                           class="pipeline-assembly-margin"
+        <pipeline-assembly fxFlex="100" style="margin-left: 10px;"
                            [rawPipelineModel]="rawPipelineModel"
                            [allElements]="allElements"
-                           [currentModifiedPipelineId]="currentModifiedPipelineId"
-                           (pipelineCanvasMaximizedEmitter)="togglePipelineCanvasMode($event)">
+                           [currentModifiedPipelineId]="currentModifiedPipelineId">
         </pipeline-assembly>
     </div>
 </div>
diff --git a/ui/src/app/editor/editor.component.scss b/ui/src/app/editor/editor.component.scss
index d21fab4..58b8fb3 100644
--- a/ui/src/app/editor/editor.component.scss
+++ b/ui/src/app/editor/editor.component.scss
@@ -30,10 +30,6 @@
   border: 1px solid var(--color-bg-2);
 }
 
-.pipeline-assembly-margin {
-  margin-top: 10px;
-}
-
 .editor-canvas-options {
   padding: 0;
   background-color: var(--color-bg-1);
diff --git a/ui/src/app/editor/editor.component.ts b/ui/src/app/editor/editor.component.ts
index f179da0..454c9ac 100644
--- a/ui/src/app/editor/editor.component.ts
+++ b/ui/src/app/editor/editor.component.ts
@@ -18,224 +18,149 @@
 
 import { Component, OnInit } from '@angular/core';
 import { EditorService } from './services/editor.service';
-import {
-  DataProcessorInvocation,
-  DataSinkInvocation,
-  DataSourceDescription,
-  SpDataSet,
-  SpDataStream,
-  PipelineElementService
-} from '@streampipes/platform-services';
-import {
-  PipelineElementConfig,
-  PipelineElementIdentifier,
-  PipelineElementUnion,
-  TabsModel
-} from './model/editor.model';
+import { DataSourceDescription, PipelineElementService, SpDataStream } from '@streampipes/platform-services';
+import { PipelineElementConfig, PipelineElementUnion } from './model/editor.model';
 import { PanelType } from '../core-ui/dialog/base-dialog/base-dialog.model';
 import { WelcomeTourComponent } from './dialog/welcome-tour/welcome-tour.component';
 import { DialogService } from '../core-ui/dialog/base-dialog/base-dialog.service';
 import { MissingElementsForTutorialComponent } from './dialog/missing-elements-for-tutorial/missing-elements-for-tutorial.component';
 import { ShepherdService } from '../services/tour/shepherd.service';
 import { ActivatedRoute } from '@angular/router';
-import { EditorConstants } from './constants/editor.constants';
 import { AuthService } from '../services/auth.service';
+import { zip } from 'rxjs';
 
 @Component({
-    selector: 'editor',
-    templateUrl: './editor.component.html',
-    styleUrls: ['./editor.component.scss']
+  selector: 'editor',
+  templateUrl: './editor.component.html',
+  styleUrls: ['./editor.component.scss']
 })
 export class EditorComponent implements OnInit {
 
-    selectedIndex = 1;
-    activeType: PipelineElementIdentifier = EditorConstants.DATA_STREAM_IDENTIFIER;
-    activeShorthand: string;
-
-    availableDataSets: SpDataSet[] = [];
-    availableDataStreams: SpDataStream[] = [];
-    availableDataProcessors: DataProcessorInvocation[] = [];
-    availableDataSinks: DataSinkInvocation[] = [];
-
-    allElements: PipelineElementUnion[] = [];
-    currentElements: (SpDataStream | DataProcessorInvocation | DataSinkInvocation)[] = [];
-
-    rawPipelineModel: PipelineElementConfig[] = [];
-    currentModifiedPipelineId: string;
-
-    elementsLoaded = [false, false, false];
-    allElementsLoaded = false;
-
-    requiredStreamForTutorialAppId: any = 'org.apache.streampipes.sources.simulator.flowrate1';
-    requiredProcessorForTutorialAppId: any = 'org.apache.streampipes.processors.filters.jvm.numericalfilter';
-    requiredSinkForTutorialAppId: any = 'org.apache.streampipes.sinks.internal.jvm.dashboard';
-    missingElementsForTutorial: any = [];
-
-    pipelineCanvasMaximized = false;
-
-    isTutorialOpen = false;
-
-    tabs: TabsModel[] = [
-        {
-            title: 'Data Sets',
-            type: EditorConstants.DATA_SET_IDENTIFIER,
-            shorthand: 'set'
-        },
-        {
-            title: 'Data Streams',
-            type: EditorConstants.DATA_STREAM_IDENTIFIER,
-            shorthand: 'stream'
-        },
-        {
-            title: 'Data Processors',
-            type: EditorConstants.DATA_PROCESSOR_IDENTIFIER,
-            shorthand: 'sepa'
-        },
-        {
-            title: 'Data Sinks',
-            type: EditorConstants.DATA_SINK_IDENTIFIER,
-            shorthand: 'action'
-        }
-    ];
-
-    constructor(private editorService: EditorService,
-                private pipelineElementService: PipelineElementService,
-                private authService: AuthService,
-                private dialogService: DialogService,
-                private shepherdService: ShepherdService,
-                private activatedRoute: ActivatedRoute) {
-    }
-
-    ngOnInit() {
-        this.activatedRoute.queryParams.subscribe(params => {
-            if (params['pipeline']) {
-                this.currentModifiedPipelineId = params['pipeline'];
-            }
-        });
-        this.pipelineElementService.getDataProcessors().subscribe(processors => {
-            this.availableDataProcessors = processors;
-            this.allElements = this.allElements.concat(processors);
-            this.afterPipelineElementLoaded(0);
+  allElements: PipelineElementUnion[] = [];
+
+  rawPipelineModel: PipelineElementConfig[] = [];
+  currentModifiedPipelineId: string;
+
+  allElementsLoaded = false;
+
+  requiredStreamForTutorialAppId: any = 'org.apache.streampipes.sources.simulator.flowrate1';
+  requiredProcessorForTutorialAppId: any = 'org.apache.streampipes.processors.filters.jvm.numericalfilter';
+  requiredSinkForTutorialAppId: any = 'org.apache.streampipes.sinks.internal.jvm.dashboard';
+  missingElementsForTutorial: any = [];
+
+  isTutorialOpen = false;
+
+  constructor(private editorService: EditorService,
+              private pipelineElementService: PipelineElementService,
+              private authService: AuthService,
+              private dialogService: DialogService,
+              private shepherdService: ShepherdService,
+              private activatedRoute: ActivatedRoute) {
+  }
+
+  ngOnInit() {
+    this.activatedRoute.queryParams.subscribe(params => {
+      if (params['pipeline']) {
+        this.currentModifiedPipelineId = params['pipeline'];
+      }
+    });
+    zip(this.pipelineElementService.getDataStreams(),
+      this.pipelineElementService.getDataProcessors(),
+      this.pipelineElementService.getDataSinks()).subscribe(response => {
+      this.allElements = this.allElements
+        .concat(response[0])
+        .concat(response[1])
+        .concat(response[2])
+        .sort((a, b) => {
+          return a.name.localeCompare(b.name);
         });
-        this.pipelineElementService.getDataStreams().subscribe(streams => {
-            // let allStreams = this.collectStreams(sources);
-            this.availableDataStreams = streams.filter(s => !(s instanceof SpDataSet));
-            this.availableDataSets = streams
-                .filter(s => s instanceof SpDataSet)
-                .map(s => s as SpDataSet);
-            this.allElements = this.allElements.concat(this.availableDataStreams);
-            this.allElements = this.allElements.concat(this.availableDataSets);
-
-            this.selectPipelineElements(1);
-            this.afterPipelineElementLoaded(1);
+      this.allElementsLoaded = true;
+      this.checkForTutorial();
+    });
+  }
+
+  checkForTutorial() {
+    const currentUser = this.authService.getCurrentUser();
+    if (currentUser.showTutorial && !this.isTutorialOpen) {
+      if (this.requiredPipelineElementsForTourPresent()) {
+        this.isTutorialOpen = true;
+        this.dialogService.open(WelcomeTourComponent, {
+          panelType: PanelType.STANDARD_PANEL,
+          title: 'Welcome to StreamPipes',
+          data: {
+            'userInfo': currentUser
+          }
         });
-        this.pipelineElementService.getDataSinks().subscribe(sinks => {
-            this.availableDataSinks = sinks;
-            this.allElements = this.allElements.concat(this.availableDataSinks);
-            this.afterPipelineElementLoaded(2);
-        });
-
+      }
     }
-
-    afterPipelineElementLoaded(index: number) {
-        this.elementsLoaded[index] = true;
-        if (this.elementsLoaded.every(e => e === true)) {
-            this.allElementsLoaded = true;
-            this.checkForTutorial();
-        }
-    }
-
-    checkForTutorial() {
-        const currentUser = this.authService.getCurrentUser();
-        if (currentUser.showTutorial && !this.isTutorialOpen) {
-            if (this.requiredPipelineElementsForTourPresent()) {
-                this.isTutorialOpen = true;
-                this.dialogService.open(WelcomeTourComponent, {
-                    panelType: PanelType.STANDARD_PANEL,
-                    title: 'Welcome to StreamPipes',
-                    data: {
-                        'userInfo': currentUser
-                    }
-                });
-            }
-        }
-    }
-
-    collectStreams(sources: DataSourceDescription[]): SpDataStream[] {
-        const streams: SpDataStream[] = [];
-        sources.forEach(source => {
-            source.spDataStreams.forEach(stream => {
-                streams.push(stream);
-            });
+  }
+
+  collectStreams(sources: DataSourceDescription[]): SpDataStream[] {
+    const streams: SpDataStream[] = [];
+    sources.forEach(source => {
+      source.spDataStreams.forEach(stream => {
+        streams.push(stream);
+      });
+    });
+    return streams;
+  }
+
+  selectPipelineElements(index: number) {
+    // this.shepherdService.trigger('select-' + this.activeShorthand);
+  }
+
+  startCreatePipelineTour() {
+    if (this.requiredPipelineElementsForTourPresent()) {
+      this.shepherdService.startCreatePipelineTour();
+    } else {
+      this.missingElementsForTutorial = [];
+      if (!this.requiredStreamForTourPresent()) {
+        this.missingElementsForTutorial.push({'name': 'Flow Rate 1', 'appId': this.requiredStreamForTutorialAppId});
+      }
+      if (!this.requiredProcessorForTourPresent()) {
+        this.missingElementsForTutorial.push({
+          'name': 'Numerical Filter',
+          'appId': this.requiredProcessorForTutorialAppId
         });
-        return streams;
-    }
-
-    selectPipelineElements(index: number) {
-        this.selectedIndex = index;
-        this.activeType = this.tabs[index].type;
-        this.activeShorthand = this.tabs[index].shorthand;
-        this.currentElements = this.allElements
-            .filter(pe => pe['@class'] === this.activeType)
-            .sort((a, b) => {
-                return a.name.localeCompare(b.name);
-            });
-        this.shepherdService.trigger('select-' + this.activeShorthand);
-    }
-
-    startCreatePipelineTour() {
-        if (this.requiredPipelineElementsForTourPresent()) {
-            this.shepherdService.startCreatePipelineTour();
-        } else {
-            this.missingElementsForTutorial = [];
-            if (!this.requiredStreamForTourPresent()) {
-                this.missingElementsForTutorial.push({'name' : 'Flow Rate 1', 'appId' : this.requiredStreamForTutorialAppId });
-            }
-            if (!this.requiredProcessorForTourPresent()) {
-                this.missingElementsForTutorial.push({'name' : 'Numerical Filter', 'appId' : this.requiredProcessorForTutorialAppId});
-            }
-            if (!this.requiredSinkForTourPresent()) {
-                this.missingElementsForTutorial.push({'name' : 'Dashboard Sink', 'appId' : this.requiredSinkForTutorialAppId});
-            }
-
-            this.dialogService.open(MissingElementsForTutorialComponent, {
-                panelType: PanelType.STANDARD_PANEL,
-                title: 'Tutorial requires pipeline elements',
-                data: {
-                    'missingElementsForTutorial': this.missingElementsForTutorial
-                }
-            });
+      }
+      if (!this.requiredSinkForTourPresent()) {
+        this.missingElementsForTutorial.push({'name': 'Dashboard Sink', 'appId': this.requiredSinkForTutorialAppId});
+      }
+
+      this.dialogService.open(MissingElementsForTutorialComponent, {
+        panelType: PanelType.STANDARD_PANEL,
+        title: 'Tutorial requires pipeline elements',
+        data: {
+          'missingElementsForTutorial': this.missingElementsForTutorial
         }
+      });
     }
-
-    requiredPipelineElementsForTourPresent() {
-        return this.requiredStreamForTourPresent() &&
-            this.requiredProcessorForTourPresent() &&
-            this.requiredSinkForTourPresent();
-    }
-
-    requiredStreamForTourPresent() {
-        return this.requiredPeForTourPresent(this.allElements,
-            this.requiredStreamForTutorialAppId);
-    }
-
-    requiredProcessorForTourPresent() {
-        return this.requiredPeForTourPresent(this.allElements,
-            this.requiredProcessorForTutorialAppId);
-    }
-
-    requiredSinkForTourPresent() {
-        return this.requiredPeForTourPresent(this.allElements,
-            this.requiredSinkForTutorialAppId);
-    }
-
-    requiredPeForTourPresent(list, appId) {
-        return list && list.some(el => {
-            return el.appId === appId;
-        });
-    }
-
-    togglePipelineCanvasMode(maximize: boolean) {
-        this.pipelineCanvasMaximized = maximize;
-    }
+  }
+
+  requiredPipelineElementsForTourPresent() {
+    return this.requiredStreamForTourPresent() &&
+      this.requiredProcessorForTourPresent() &&
+      this.requiredSinkForTourPresent();
+  }
+
+  requiredStreamForTourPresent() {
+    return this.requiredPeForTourPresent(this.allElements,
+      this.requiredStreamForTutorialAppId);
+  }
+
+  requiredProcessorForTourPresent() {
+    return this.requiredPeForTourPresent(this.allElements,
+      this.requiredProcessorForTutorialAppId);
+  }
+
+  requiredSinkForTourPresent() {
+    return this.requiredPeForTourPresent(this.allElements,
+      this.requiredSinkForTutorialAppId);
+  }
+
+  requiredPeForTourPresent(list, appId) {
+    return list && list.some(el => {
+      return el.appId === appId;
+    });
+  }
 }
diff --git a/ui/src/app/editor/editor.module.ts b/ui/src/app/editor/editor.module.ts
index 8caf08e..cf19419 100644
--- a/ui/src/app/editor/editor.module.ts
+++ b/ui/src/app/editor/editor.module.ts
@@ -67,6 +67,11 @@ import { PipelineElementPreviewComponent } from './components/pipeline-element-p
 import { PipelineElementDiscoveryComponent } from './dialog/pipeline-element-discovery/pipeline-element-discovery.component';
 import { PipelineStyleService } from './services/pipeline-style.service';
 import { PlatformServicesModule } from '@streampipes/platform-services';
+import { PipelineElementIconStandRowComponent } from './components/pipeline-element-icon-stand-row/pipeline-element-icon-stand-row.component';
+import { PipelineElementTypeFilterPipe } from './services/pipeline-element-type-filter.pipe';
+import { PipelineElementNameFilterPipe } from "./services/pipeline-element-name-filter.pipe";
+import { PipelineElementGroupFilterPipe } from "./services/pipeline-element-group-filter.pipe";
+
 
 @NgModule({
   imports: [
@@ -102,10 +107,14 @@ import { PlatformServicesModule } from '@streampipes/platform-services';
     PipelineElementDiscoveryComponent,
     PipelineElementDocumentationComponent,
     PipelineElementIconStandComponent,
+    PipelineElementIconStandRowComponent,
+    PipelineElementGroupFilterPipe,
+    PipelineElementNameFilterPipe,
     PipelineElementOptionsComponent,
     PipelineElementPreviewComponent,
     PipelineElementRecommendationComponent,
     PipelineElementTemplateConfigComponent,
+    PipelineElementTypeFilterPipe,
     PipelineComponent,
     PropertySelectionComponent,
     SavePipelineComponent,
diff --git a/ui/src/app/editor/model/editor.model.ts b/ui/src/app/editor/model/editor.model.ts
index 8cc5059..45945be 100644
--- a/ui/src/app/editor/model/editor.model.ts
+++ b/ui/src/app/editor/model/editor.model.ts
@@ -16,12 +16,7 @@
  *
  */
 
-import {
-  DataProcessorInvocation,
-  DataSinkInvocation,
-  SpDataSet,
-  SpDataStream
-} from '@streampipes/platform-services';
+import { DataProcessorInvocation, DataSinkInvocation, SpDataSet, SpDataStream } from '@streampipes/platform-services';
 import { InjectionToken } from '@angular/core';
 
 export interface PipelineElementHolder {
@@ -91,3 +86,9 @@ export type PipelineElementIdentifier = 'org.apache.streampipes.model.SpDataStre
     | 'org.apache.streampipes.model.SpDataSet'
     | 'org.apache.streampipes.model.graph.DataProcessorInvocation'
     | 'org.apache.streampipes.model.graph.DataSinkInvocation';
+
+export interface PeCategory {
+  code: string;
+  label: string;
+  description: string;
+}
diff --git a/ui/src/app/editor/services/editor.service.ts b/ui/src/app/editor/services/editor.service.ts
index 7611431..659b081 100644
--- a/ui/src/app/editor/services/editor.service.ts
+++ b/ui/src/app/editor/services/editor.service.ts
@@ -27,12 +27,12 @@ import {
   PipelineElementRecommendationMessage,
   PipelineModificationMessage,
   PipelinePreviewModel,
+  PlatformServicesCommons,
   SpDataSet,
-  SpDataStream,
-  PlatformServicesCommons
+  SpDataStream
 } from '@streampipes/platform-services';
 import { Observable, Subject } from 'rxjs';
-import { PipelineElementConfig, PipelineElementUnion } from '../model/editor.model';
+import { PeCategory, PipelineElementConfig, PipelineElementUnion } from '../model/editor.model';
 import { PanelType } from '../../core-ui/dialog/base-dialog/base-dialog.model';
 import { DialogService } from '../../core-ui/dialog/base-dialog/base-dialog.service';
 import { HelpComponent } from '../dialog/help/help.component';
@@ -106,16 +106,19 @@ export class EditorService {
       }
     }
 
-    getEpCategories() {
-        return this.http.get(this.platformServicesCommons.apiBasePath + '/categories/ep');
+    getEpCategories(): Observable<PeCategory[]> {
+        return this.http.get(this.platformServicesCommons.apiBasePath + '/categories/ep')
+          .pipe(map(response => response as PeCategory[]));
     }
 
-    getEpaCategories() {
-        return this.http.get(this.platformServicesCommons.apiBasePath + '/categories/epa');
+    getEpaCategories(): Observable<PeCategory[]> {
+        return this.http.get(this.platformServicesCommons.apiBasePath + '/categories/epa')
+          .pipe(map(response => response as PeCategory[]));
     }
 
-    getEcCategories() {
-        return this.http.get(this.platformServicesCommons.apiBasePath + '/categories/ec');
+    getEcCategories(): Observable<PeCategory[]> {
+        return this.http.get(this.platformServicesCommons.apiBasePath + '/categories/ec')
+          .pipe(map(response => response as PeCategory[]));
     }
 
     updateCachedPipeline(rawPipelineModel: any) {
diff --git a/ui/src/app/editor/components/pipeline-element-icon-stand/pipeline-element-icon-stand.component.scss b/ui/src/app/editor/services/pipeline-element-group-filter.pipe.ts
similarity index 56%
copy from ui/src/app/editor/components/pipeline-element-icon-stand/pipeline-element-icon-stand.component.scss
copy to ui/src/app/editor/services/pipeline-element-group-filter.pipe.ts
index d5a2288..29e3ff7 100644
--- a/ui/src/app/editor/components/pipeline-element-icon-stand/pipeline-element-icon-stand.component.scss
+++ b/ui/src/app/editor/services/pipeline-element-group-filter.pipe.ts
@@ -16,23 +16,21 @@
  *
  */
 
-@import '../../../../scss/_variables.scss';
+import { Pipe, PipeTransform } from '@angular/core';
+import { PeCategory, PipelineElementUnion } from '../model/editor.model';
 
-.menu-text-color {
-    color: var(--color-accent);
-    display: flex;
-    align-items: center;
-}
+@Pipe({name: 'pipelineElementGroup'})
+export class PipelineElementGroupFilterPipe implements PipeTransform {
 
-.icon-stand {
-    max-height: 200px;
-    padding-bottom: 1%;
-    overflow-y: auto;
-    border-top: 0px;
-    background: var(--color-bg-1);
-}
+  transform(pipelineElements: PipelineElementUnion[],
+            category: PeCategory): PipelineElementUnion[] {
+    return pipelineElements.filter(pe => {
+      if ((!pe.category || pe.category.length === 0) && category.code === 'UNCATEGORIZED') {
+        return true;
+      } else {
+        return pe.category.find(c => c === category.code) !== undefined;
+      }
+    });
+  }
 
-.border {
-    border: 1px solid var(--color-bg-2);
-    border-top: 0;
 }
diff --git a/ui/src/app/editor/editor.component.scss b/ui/src/app/editor/services/pipeline-element-name-filter.pipe.ts
similarity index 57%
copy from ui/src/app/editor/editor.component.scss
copy to ui/src/app/editor/services/pipeline-element-name-filter.pipe.ts
index d21fab4..2d27fc1 100644
--- a/ui/src/app/editor/editor.component.scss
+++ b/ui/src/app/editor/services/pipeline-element-name-filter.pipe.ts
@@ -16,25 +16,22 @@
  *
  */
 
-@import '../../scss/variables';
+import { Pipe, PipeTransform } from '@angular/core';
+import { PipelineElementUnion } from '../model/editor.model';
 
-.text-color {
-  color: var(--color-accent);
-}
+@Pipe({name: 'pipelineElementName'})
+export class PipelineElementNameFilterPipe implements PipeTransform {
 
-.page-container {
-  border: 0;
-}
+  transform(pipelineElements: PipelineElementUnion[],
+            filter: string): PipelineElementUnion[] {
+    return pipelineElements.filter(pe => {
+      return this.matchesText(filter, pe);
+    });
+  }
 
-.border {
-  border: 1px solid var(--color-bg-2);
-}
-
-.pipeline-assembly-margin {
-  margin-top: 10px;
-}
+  matchesText(filter: string,
+              el: PipelineElementUnion): boolean {
+    return filter === '' || el.name.toLowerCase().includes(filter.toLowerCase());
+  }
 
-.editor-canvas-options {
-  padding: 0;
-  background-color: var(--color-bg-1);
 }
diff --git a/ui/src/app/editor/editor.component.scss b/ui/src/app/editor/services/pipeline-element-type-filter.pipe.ts
similarity index 56%
copy from ui/src/app/editor/editor.component.scss
copy to ui/src/app/editor/services/pipeline-element-type-filter.pipe.ts
index d21fab4..3a8507c 100644
--- a/ui/src/app/editor/editor.component.scss
+++ b/ui/src/app/editor/services/pipeline-element-type-filter.pipe.ts
@@ -16,25 +16,19 @@
  *
  */
 
-@import '../../scss/variables';
+import { Pipe, PipeTransform } from '@angular/core';
+import { PipelineElementType, PipelineElementUnion } from '../model/editor.model';
+import { PipelineElementTypeUtils } from '../utils/editor.utils';
 
-.text-color {
-  color: var(--color-accent);
-}
-
-.page-container {
-  border: 0;
-}
+@Pipe({name: 'pipelineElementType'})
+export class PipelineElementTypeFilterPipe implements PipeTransform {
 
-.border {
-  border: 1px solid var(--color-bg-2);
-}
-
-.pipeline-assembly-margin {
-  margin-top: 10px;
-}
+  transform(pipelineElements: PipelineElementUnion[],
+            allowedTypes: PipelineElementType[]): PipelineElementUnion[] {
+    return pipelineElements.filter(pe => {
+      const peType = PipelineElementTypeUtils.fromClassName(pe['@class']);
+      return allowedTypes.find(t => t === peType) !== undefined;
+    });
+  }
 
-.editor-canvas-options {
-  padding: 0;
-  background-color: var(--color-bg-1);
 }
diff --git a/ui/src/app/services/get-element-icon-text.service.ts b/ui/src/app/services/get-element-icon-text.service.ts
index ba833f3..eeff6a6 100644
--- a/ui/src/app/services/get-element-icon-text.service.ts
+++ b/ui/src/app/services/get-element-icon-text.service.ts
@@ -25,12 +25,12 @@ export class ElementIconText {
 
     getElementIconText(s) {
         let result = '';
-        if (s.length <= 4) {
+        if (s.length <= 3) {
             result = s;
         } else {
             const words = s.split(' ');
             words.forEach((word, i) => {
-                if (i < 4) {
+                if (i < 3) {
                     result += word.charAt(0);
                 }
             });
diff --git a/ui/src/scss/sp/colors.scss b/ui/src/scss/sp/colors.scss
index 852dfd0..60379b9 100644
--- a/ui/src/scss/sp/colors.scss
+++ b/ui/src/scss/sp/colors.scss
@@ -112,3 +112,4 @@
 .sp-tab-bg {
     background-color: var(--color-bg-1);
 }
+
diff --git a/ui/src/scss/sp/layout.scss b/ui/src/scss/sp/layout.scss
index f664ae2..bc20f94 100644
--- a/ui/src/scss/sp/layout.scss
+++ b/ui/src/scss/sp/layout.scss
@@ -116,7 +116,7 @@
 .page-container-nav {
   line-height:24px;
   height: 50px;
-  border-bottom:1px solid var(--color-tab-border);
+  border-bottom:2px solid var(--color-bg-3);
 }
 
 .text-center {
diff --git a/ui/src/scss/sp/main.scss b/ui/src/scss/sp/main.scss
index 326c2dd..1bde8fe 100644
--- a/ui/src/scss/sp/main.scss
+++ b/ui/src/scss/sp/main.scss
@@ -37,6 +37,12 @@ body {
   font-family: 'Roboto', 'Arial', sans-serif !important;
   height: auto !important;
   width: 100%;
+
+  --color-set: #{$sp-color-set};
+  --color-stream: #{$sp-color-stream};
+  --color-processor: #{$sp-color-processor};
+  --color-sink: #{$sp-color-sink};
+  --color-tab-border: var(--color-bg-3);
 }
 
 pre.version {
@@ -892,6 +898,7 @@ md-select.md-default-theme .md-select-value.md-select-placeholder, md-select .md
   /* 	border-bottom: 2px solid rgb(63,81,181); */
   color: black;
   background: var(--color-bg-1);
+  border-bottom: 1px solid var(--color-bg-3);
   padding: 0px;
   /* 	margin-top: 1%;  */
 }
diff --git a/ui/src/scss/sp/pipeline-element-options.scss b/ui/src/scss/sp/pipeline-element-options.scss
index 813ed34..014ca98 100644
--- a/ui/src/scss/sp/pipeline-element-options.scss
+++ b/ui/src/scss/sp/pipeline-element-options.scss
@@ -66,11 +66,8 @@
 .help-button-icon-stand {
   display: block;
   cursor: pointer;
-  position: relative;
-  left: 20px;
-  top: 40px;
-  height: 40px;
-  width: 40px;
+  height: 20px;
+  width: 20px;
   border-radius: 50%;
   background-color: $sp-color-accent;
   color: rgba(255, 255, 255, 0.75);