From e7ba988cd03e213737dd8d7188a134fb5d59d235 Mon Sep 17 00:00:00 2001
From: rhenck <richard.henck@iqb.hu-berlin.de>
Date: Thu, 5 Aug 2021 16:19:19 +0200
Subject: [PATCH] [editor] Optimize component parameters and refactor unit
 service

Also refactor unit service to use more it's own selection variable
instead of using passed references for manipulating objects.
---
 .../canvas/canvas-element-overlay.ts          |  21 +-
 .../canvas/page-canvas.component.html         |   8 +-
 .../page-view/canvas/page-canvas.component.ts |  28 +-
 .../page-view/canvas/selection.service.ts     |  50 --
 .../page-view/page-view.component.ts          |   5 +-
 .../element-properties.component.ts           | 568 +++++++++---------
 .../properties/page-properties.component.ts   |  22 +-
 .../properties/properties.component.html      |  10 +-
 .../properties/properties.component.ts        |  16 +-
 .../section-properties.component.ts           |  30 +-
 .../unit-view/unit-view.component.html        |   2 +-
 projects/editor/src/app/unit.service.ts       | 123 ++--
 12 files changed, 407 insertions(+), 476 deletions(-)
 delete mode 100644 projects/editor/src/app/components/unit-view/page-view/canvas/selection.service.ts

diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/canvas-element-overlay.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/canvas-element-overlay.ts
index a1ce7447d..c55db2dc6 100644
--- a/projects/editor/src/app/components/unit-view/page-view/canvas/canvas-element-overlay.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/canvas/canvas-element-overlay.ts
@@ -1,16 +1,14 @@
 import {
+  Directive, Input,
   ComponentFactoryResolver, ComponentRef,
-  Directive,
-  EventEmitter,
   HostListener,
-  Input,
-  Output,
   ViewChild, ViewContainerRef
 } from '@angular/core';
 import { Subject } from 'rxjs';
 import { takeUntil } from 'rxjs/operators';
-import { UnitUIElement } from '../../../../../../../common/unit';
+// eslint-disable-next-line import/no-cycle
 import { UnitService } from '../../../../unit.service';
+import { UnitUIElement } from '../../../../../../../common/unit';
 import * as ComponentUtils from '../../../../../../../common/component-utils';
 import { FormElementComponent } from '../../../../../../../common/form-element-component.directive';
 import { ValueChangeElement } from '../../../../../../../common/form';
@@ -19,9 +17,6 @@ import { ElementComponent } from '../../../../../../../common/element-component.
 @Directive()
 export abstract class CanvasElementOverlay {
   @Input() element!: UnitUIElement;
-  @Output() elementSelected = new EventEmitter<{
-    componentElement: CanvasElementOverlay,
-    multiSelect: boolean }>();
   @ViewChild('elementContainer', { read: ViewContainerRef, static: true }) private elementContainer!: ViewContainerRef;
   selected = false;
   protected childComponent!: ComponentRef<ElementComponent>;
@@ -57,7 +52,7 @@ export abstract class CanvasElementOverlay {
     if (!(event.target as Element).tagName.includes('input'.toUpperCase()) &&
       !(event.target as Element).tagName.includes('textarea'.toUpperCase()) &&
       event.key === 'Delete') {
-      this.unitService.deleteElement(this.element);
+      this.unitService.deleteSelectedElements();
     }
   }
 
@@ -67,13 +62,9 @@ export abstract class CanvasElementOverlay {
 
   click(event: MouseEvent): void {
     if (event.shiftKey) {
-      this.elementSelected.emit({
-        componentElement: this, multiSelect: true
-      });
+      this.unitService.selectElement({ componentElement: this, multiSelect: true });
     } else {
-      this.elementSelected.emit({
-        componentElement: this, multiSelect: false
-      });
+      this.unitService.selectElement({ componentElement: this, multiSelect: false });
     }
   }
 
diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.html b/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.html
index bff14d451..58eca749e 100644
--- a/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.html
+++ b/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.html
@@ -6,14 +6,8 @@
       <div #section_component app-canvas-section class="section"
            *ngFor="let section of page.sections; let i = index"
            [section]="section" [sectionIndex]="i"
-           (click)="selectSection(i)"
            cdkDropList cdkDropListSortingDisabled
-           (cdkDropListDropped)="elementDropped($event)" [cdkDropListData]="section"
-           [ngStyle]="{
-                  border: i === (unitService.selectedPageSectionIndex | async) ? '1px solid': '1px dotted',
-                  'width.px': section.width,
-                  'height.px': section.height,
-                  'background-color': section.backgroundColor}">
+           (cdkDropListDropped)="elementDropped($event)" [cdkDropListData]="section">
       </div>
     </div>
   </div>
diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.ts
index d1af3d84a..7f1c83332 100644
--- a/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.ts
@@ -1,9 +1,6 @@
 import {
-  Component, OnInit, OnDestroy,
-  Input, QueryList, ViewChildren
+  Component, Input, QueryList, ViewChildren
 } from '@angular/core';
-import { Subject } from 'rxjs';
-import { takeUntil } from 'rxjs/operators';
 import { CdkDragDrop, transferArrayItem } from '@angular/cdk/drag-drop';
 import { UnitPage, UnitPageSection } from '../../../../../../../common/unit';
 import { UnitService } from '../../../../unit.service';
@@ -17,26 +14,12 @@ import { CanvasSectionComponent } from './canvas-section.component';
     '.canvasBackground {background-color: lightgrey; padding:20px; height: 100%; overflow: auto;}'
   ]
 })
-export class PageCanvasComponent implements OnInit, OnDestroy {
-  @Input() pageIndex!: number;
+export class PageCanvasComponent {
+  @Input() page!: UnitPage;
   @ViewChildren('section_component') canvasSections!: QueryList<CanvasSectionComponent>;
-  page!: UnitPage;
-  private ngUnsubscribe = new Subject<void>();
 
   constructor(public unitService: UnitService) { }
 
-  ngOnInit(): void {
-    this.unitService.getPageObservable(this.pageIndex)
-      .pipe(takeUntil(this.ngUnsubscribe))
-      .subscribe((page: UnitPage) => {
-        this.page = page;
-      });
-  }
-
-  selectSection(id: number): void {
-    this.unitService.updatePageSectionSelection(Number(id));
-  }
-
   elementDropped(event: CdkDragDrop<UnitPageSection>): void {
     const sourceItemModel = event.item.data;
 
@@ -70,9 +53,4 @@ export class PageCanvasComponent implements OnInit, OnDestroy {
     const reduceFct = (accumulator: number, currentValue: UnitPageSection) => accumulator + currentValue.height;
     return this.page.sections.reduce(reduceFct, 0);
   }
-
-  ngOnDestroy(): void {
-    this.ngUnsubscribe.next();
-    this.ngUnsubscribe.complete();
-  }
 }
diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/selection.service.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/selection.service.ts
deleted file mode 100644
index 21e0bb04d..000000000
--- a/projects/editor/src/app/components/unit-view/page-view/canvas/selection.service.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { Injectable, OnDestroy } from '@angular/core';
-import { Subject } from 'rxjs';
-import { takeUntil } from 'rxjs/operators';
-import { UnitService } from '../../../../unit.service';
-import { UnitUIElement } from '../../../../../../../common/unit';
-import { CanvasElementOverlay } from './canvas-element-overlay';
-
-@Injectable({
-  providedIn: 'root'
-})
-export class SelectionService implements OnDestroy { // TODO selectionService
-  private ngUnsubscribe = new Subject<void>();
-  selectedComponentElements: CanvasElementOverlay[] = [];
-
-  elementSelected: Subject<UnitUIElement[]> = new Subject<UnitUIElement[]>();
-
-  constructor(private unitService: UnitService) {
-    this.unitService.selectedPageIndex
-      .pipe(takeUntil(this.ngUnsubscribe))
-      .subscribe(() => {
-        this.clearSelection();
-      });
-  }
-
-  getSelectedElements(): UnitUIElement[] {
-    return this.selectedComponentElements.map(element => element.element);
-  }
-
-  selectElement(event: { componentElement: CanvasElementOverlay; multiSelect: boolean }): void {
-    if (!event.multiSelect) {
-      this.clearSelection();
-    }
-    this.selectedComponentElements.push(event.componentElement);
-    event.componentElement.setSelected(true); // TODO direkt in der component?
-
-    this.elementSelected.next(this.selectedComponentElements.map(componentElement => componentElement.element));
-  }
-
-  private clearSelection() {
-    this.selectedComponentElements.forEach((overlayComponent: CanvasElementOverlay) => {
-      overlayComponent.setSelected(false);
-    });
-    this.selectedComponentElements = [];
-  }
-
-  ngOnDestroy(): void {
-    this.ngUnsubscribe.next();
-    this.ngUnsubscribe.complete();
-  }
-}
diff --git a/projects/editor/src/app/components/unit-view/page-view/page-view.component.ts b/projects/editor/src/app/components/unit-view/page-view/page-view.component.ts
index ec08a3851..ecf2c1a52 100644
--- a/projects/editor/src/app/components/unit-view/page-view/page-view.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/page-view.component.ts
@@ -1,16 +1,17 @@
 import {
   Component, Input
 } from '@angular/core';
+import { UnitPage } from '../../../../../../common/unit';
 
 @Component({
   selector: 'app-page-view',
   template: `
-    <app-page-canvas [pageIndex]="pageIndex" fxLayout="column"></app-page-canvas>
+    <app-page-canvas [page]="page" fxLayout="column"></app-page-canvas>
   `,
   styles: [
     'app-page-canvas {height: 100%; display: block}'
   ]
 })
 export class PageViewComponent {
-  @Input() pageIndex!: number;
+  @Input() page!: UnitPage;
 }
diff --git a/projects/editor/src/app/components/unit-view/page-view/properties/element-properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties/element-properties.component.ts
index 688de1735..65d9da57b 100644
--- a/projects/editor/src/app/components/unit-view/page-view/properties/element-properties.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/properties/element-properties.component.ts
@@ -5,307 +5,307 @@ import { Subject } from 'rxjs';
 import { takeUntil } from 'rxjs/operators';
 import { UnitUIElement } from '../../../../../../../common/unit';
 import { UnitService } from '../../../../unit.service';
-import { SelectionService } from '../canvas/selection.service';
 
 @Component({
   selector: 'app-element-properties',
   template: `
-    <mat-tab-group mat-stretch-tabs dynamicHeight>
-      <mat-tab>
-        <ng-template mat-tab-label>
-          <mat-icon class="example-tab-icon">build</mat-icon>
-        </ng-template>
-        <div fxLayout="column">
-          <mat-form-field *ngIf="combinedProperties.hasOwnProperty('id')">
-            <mat-label>ID</mat-label>
-            <input matInput type="text" *ngIf="selectedElements.length === 1" [value]="combinedProperties.id"
-                   (input)="updateModel('id', $any($event.target).value)">
-            <input matInput type="text" disabled *ngIf="selectedElements.length > 1" [value]="'Muss eindeutig sein'">
-          </mat-form-field>
-
-          <mat-form-field *ngIf="combinedProperties.hasOwnProperty('label')">
-            <mat-label>Label</mat-label>
-            <input matInput type="text" [value]="combinedProperties.label"
-                   (input)="updateModel('label', $any($event.target).value)">
-          </mat-form-field>
-
-          <mat-form-field *ngIf="combinedProperties.hasOwnProperty('text')">
-            <mat-label>Text</mat-label>
-            <textarea matInput type="text" cdkTextareaAutosize [value]="combinedProperties.text"
-                      (input)="updateModel('text', $any($event.target).value)">
-          </textarea>
-          </mat-form-field>
-
-          <mat-form-field *ngIf="combinedProperties.type === 'text-field'">
-            <mat-label>Vorbelegung</mat-label>
-            <input matInput type="text"
-                   [value]="combinedProperties.value"
-                   (input)="updateModel('value', $any($event.target).value)">
-          </mat-form-field>
-          <section *ngIf="combinedProperties.type === 'checkbox'">
-            Vorbelegung
-            <mat-button-toggle-group (change)="transformToBoolAndUpdateModel('value', $event.value)">
-              <mat-button-toggle value="true">wahr</mat-button-toggle>
-              <mat-button-toggle value="false">falsch</mat-button-toggle>
-              <mat-button-toggle value="undefined">undefiniert</mat-button-toggle>
-            </mat-button-toggle-group>
-          </section>
-          <mat-form-field *ngIf="combinedProperties.type === 'dropdown' || combinedProperties.type === 'radio'"
-                          appearance="fill">
-            <mat-label>Vorbelegung</mat-label>
-            <mat-select (selectionChange)="updateModel('value', $event.value)">
-              <mat-option>undefiniert</mat-option>
-              <mat-option *ngFor="let option of $any(combinedProperties).options; let i = index" [value]="i">
-                {{option}}
-              </mat-option>
-            </mat-select>
-          </mat-form-field>
-
-          <mat-divider></mat-divider>
-
-          <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('required')"
-                        [checked]="$any(combinedProperties.required)"
-                        (change)="updateModel('required', $event.checked)">
-            Pflichtfeld
-          </mat-checkbox>
-          <mat-form-field *ngIf="combinedProperties.hasOwnProperty('requiredWarnMessage')">
-            <mat-label>Warnmeldung</mat-label>
-            <input matInput type="text" [value]="combinedProperties.requiredWarnMessage"
-                   (input)="updateModel('requiredWarnMessage', $any($event.target).value)">
-          </mat-form-field>
-
-          <mat-divider></mat-divider>
-
-
-          <mat-form-field *ngIf="combinedProperties.hasOwnProperty('min')">
-            <mat-label>Minimalwert</mat-label>
-            <input matInput type="number" [value]="combinedProperties.min"
-                   (input)="updateModel('min', $any($event.target).value)">
-          </mat-form-field>
-          <mat-form-field *ngIf="combinedProperties.hasOwnProperty('min')">
-            <mat-label>Warnmeldung</mat-label>
-            <input matInput type="text" [value]="combinedProperties.minWarnMessage"
-                   (input)="updateModel('minWarnMessage', $any($event.target).value)">
-          </mat-form-field>
-
-          <mat-divider></mat-divider>
-
-
-          <mat-form-field *ngIf="combinedProperties.hasOwnProperty('max')">
-            <mat-label>Maximalwert</mat-label>
-            <input matInput type="number" [value]="combinedProperties.max"
-                   (input)="updateModel('max', $any($event.target).value)">
-          </mat-form-field>
-          <mat-form-field *ngIf="combinedProperties.hasOwnProperty('max')">
-            <mat-label>Warnmeldung</mat-label>
-            <input matInput type="text" [value]="combinedProperties.maxWarnMessage"
-                   (input)="updateModel('maxWarnMessage', $any($event.target).value)">
-          </mat-form-field>
-
-          <mat-form-field disabled="true" *ngIf="combinedProperties.hasOwnProperty('options')">
-            <div *ngIf="combinedProperties.options !== undefined">
-              <mat-label>Optionen</mat-label>
-              <!--            TODO reorder via droplist-->
-              <mat-list *ngFor="let option of $any(combinedProperties.options)">
-                <mat-list-item>{{option}}</mat-list-item>
-                <mat-divider></mat-divider>
-              </mat-list>
-            </div>
-            <div class="newOptionElement" fxLayout="row" fxLayoutAlign="center center">
-              <button mat-icon-button matPrefix
-                      (click)="updateModel('options', newOption.value)">
-                <mat-icon>add</mat-icon>
-              </button>
-              <input #newOption matInput type="text" placeholder="Optionstext">
-            </div>
-          </mat-form-field>
-
-          <mat-form-field *ngIf="combinedProperties.hasOwnProperty('alignment')">
-            <mat-label>Ausrichtung</mat-label>
-            <mat-select [value]="combinedProperties.alignment"
-                        (change)="$any($event.target).value">
-              <mat-option *ngFor="let option of ['row', 'column']" [value]="option">
-                {{option}}
-              </mat-option>
-            </mat-select>
-          </mat-form-field>
-
-          <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('resizeEnabled')"
-                        [checked]="$any(combinedProperties.resizeEnabled)"
-                        (change)="updateModel('resizeEnabled', $event.checked)">
-            größenverstellbar
-          </mat-checkbox>
-
-          <mat-form-field disabled="true" *ngIf="combinedProperties.hasOwnProperty('sentences')">
-            <div *ngIf="combinedProperties.sentences !== undefined">
-              <mat-label>Sätze</mat-label>
-              <mat-list *ngFor="let sentence of $any(combinedProperties.sentences)">
-                <mat-list-item>{{sentence}}</mat-list-item>
-                <mat-divider></mat-divider>
-              </mat-list>
-            </div>
-            <div class="newOptionElement" fxLayout="row" fxLayoutAlign="center center">
-              <button mat-icon-button matPrefix
-                      (click)="updateModel('sentences', newOption.value)">
-                <mat-icon>add</mat-icon>
-              </button>
-              <input #newOption matInput type="text" placeholder="Optionstext">
-            </div>
-          </mat-form-field>
-
-        </div>
-        <button mat-raised-button class="delete-element-button" (click)="deleteElement()">
-          Element löschen
-        </button>
-        <button mat-raised-button class="duplicate-element-button" (click)="duplicateElement()">
-          Element duplizieren
-        </button>
-      </mat-tab>
-
-      <mat-tab>
-        <ng-template mat-tab-label>
-          <mat-icon class="example-tab-icon">format_shapes</mat-icon>
-        </ng-template>
-        <div fxLayout="column">
-
-          <ng-container *ngIf="!combinedProperties.dynamicPositioning; else elseBlock">
-            <!--        <ng-container>-->
-            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('width')">
-              <mat-label>Breite</mat-label>
-              <input matInput type="number" [value]="combinedProperties.width"
-                     (input)="updateModel('width', $any($event.target).value)">
+    <ng-container *ngIf="selectedElements">
+      <mat-tab-group mat-stretch-tabs dynamicHeight>
+        <mat-tab>
+          <ng-template mat-tab-label>
+            <mat-icon class="example-tab-icon">build</mat-icon>
+          </ng-template>
+          <div fxLayout="column">
+            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('id')">
+              <mat-label>ID</mat-label>
+              <input matInput type="text" *ngIf="selectedElements.length === 1" [value]="combinedProperties.id"
+                     (input)="updateModel('id', $any($event.target).value)">
+              <input matInput type="text" disabled *ngIf="selectedElements.length > 1" [value]="'Muss eindeutig sein'">
             </mat-form-field>
-            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('height')">
-              <mat-label>Hoehe</mat-label>
-              <input matInput type="number" [value]="combinedProperties.height"
-                     (input)="updateModel('height', $any($event.target).value)">
+
+            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('label')">
+              <mat-label>Label</mat-label>
+              <input matInput type="text" [value]="combinedProperties.label"
+                     (input)="updateModel('label', $any($event.target).value)">
             </mat-form-field>
-            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('xPosition')">
-              <mat-label>X Position</mat-label>
-              <input matInput type="number" [value]="combinedProperties.xPosition"
-                     (input)="updateModel('xPosition', $any($event.target).value)">
+
+            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('text')">
+              <mat-label>Text</mat-label>
+              <textarea matInput type="text" cdkTextareaAutosize [value]="combinedProperties.text"
+                        (input)="updateModel('text', $any($event.target).value)">
+            </textarea>
             </mat-form-field>
-            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('yPosition')">
-              <mat-label>Y Position</mat-label>
-              <input matInput type="number" [value]="combinedProperties.yPosition"
-                     (input)="updateModel('yPosition', $any($event.target).value)">
+
+            <mat-form-field *ngIf="combinedProperties.type === 'text-field'">
+              <mat-label>Vorbelegung</mat-label>
+              <input matInput type="text"
+                     [value]="combinedProperties.value"
+                     (input)="updateModel('value', $any($event.target).value)">
             </mat-form-field>
-            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('zIndex')">
-              <mat-label>Z-Index</mat-label>
-              <input matInput type="number" [value]="combinedProperties.zIndex"
-                     (input)="updateModel('zIndex', $any($event.target).value)"
-                     matTooltip="Priorität beim Stapeln von Elementen. Der höhere Index erscheint vorne.">
+            <section *ngIf="combinedProperties.type === 'checkbox'">
+              Vorbelegung
+              <mat-button-toggle-group (change)="transformToBoolAndUpdateModel('value', $event.value)">
+                <mat-button-toggle value="true">wahr</mat-button-toggle>
+                <mat-button-toggle value="false">falsch</mat-button-toggle>
+                <mat-button-toggle value="undefined">undefiniert</mat-button-toggle>
+              </mat-button-toggle-group>
+            </section>
+            <mat-form-field *ngIf="combinedProperties.type === 'dropdown' || combinedProperties.type === 'radio'"
+                            appearance="fill">
+              <mat-label>Vorbelegung</mat-label>
+              <mat-select (selectionChange)="updateModel('value', $event.value)">
+                <mat-option>undefiniert</mat-option>
+                <mat-option *ngFor="let option of $any(combinedProperties).options; let i = index" [value]="i">
+                  {{option}}
+                </mat-option>
+              </mat-select>
             </mat-form-field>
-          </ng-container>
-          <ng-template #elseBlock>
-            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('width')">
-              <mat-label>Mindestbreite</mat-label>
-              <input matInput type="number" [value]="combinedProperties.width"
-                     (input)="updateModel('width', $any($event.target).value)">
+
+            <mat-divider></mat-divider>
+
+            <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('required')"
+                          [checked]="$any(combinedProperties.required)"
+                          (change)="updateModel('required', $event.checked)">
+              Pflichtfeld
+            </mat-checkbox>
+            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('requiredWarnMessage')">
+              <mat-label>Warnmeldung</mat-label>
+              <input matInput type="text" [value]="combinedProperties.requiredWarnMessage"
+                     (input)="updateModel('requiredWarnMessage', $any($event.target).value)">
             </mat-form-field>
-            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('height')">
-              <mat-label>Mindesthoehe</mat-label>
-              <input matInput type="number" [value]="combinedProperties.height"
-                     (input)="updateModel('height', $any($event.target).value)">
+
+            <mat-divider></mat-divider>
+
+
+            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('min')">
+              <mat-label>Minimalwert</mat-label>
+              <input matInput type="number" [value]="combinedProperties.min"
+                     (input)="updateModel('min', $any($event.target).value)">
+            </mat-form-field>
+            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('min')">
+              <mat-label>Warnmeldung</mat-label>
+              <input matInput type="text" [value]="combinedProperties.minWarnMessage"
+                     (input)="updateModel('minWarnMessage', $any($event.target).value)">
             </mat-form-field>
 
-            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('gridColumnStart')">
-              <mat-label>Startspalte</mat-label>
-              <input matInput type="number" [value]="combinedProperties.gridColumnStart"
-                     (input)="updateModel('gridColumnStart', $any($event.target).value)">
+            <mat-divider></mat-divider>
+
+
+            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('max')">
+              <mat-label>Maximalwert</mat-label>
+              <input matInput type="number" [value]="combinedProperties.max"
+                     (input)="updateModel('max', $any($event.target).value)">
+            </mat-form-field>
+            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('max')">
+              <mat-label>Warnmeldung</mat-label>
+              <input matInput type="text" [value]="combinedProperties.maxWarnMessage"
+                     (input)="updateModel('maxWarnMessage', $any($event.target).value)">
             </mat-form-field>
-            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('gridColumnEnd')">
-              <mat-label>Endspalte</mat-label>
-              <input matInput type="number" [value]="combinedProperties.gridColumnEnd"
-                     (input)="updateModel('gridColumnEnd', $any($event.target).value)">
+
+            <mat-form-field disabled="true" *ngIf="combinedProperties.hasOwnProperty('options')">
+              <div *ngIf="combinedProperties.options !== undefined">
+                <mat-label>Optionen</mat-label>
+                <!--            TODO reorder via droplist-->
+                <mat-list *ngFor="let option of $any(combinedProperties.options)">
+                  <mat-list-item>{{option}}</mat-list-item>
+                  <mat-divider></mat-divider>
+                </mat-list>
+              </div>
+              <div class="newOptionElement" fxLayout="row" fxLayoutAlign="center center">
+                <button mat-icon-button matPrefix
+                        (click)="updateModel('options', newOption.value)">
+                  <mat-icon>add</mat-icon>
+                </button>
+                <input #newOption matInput type="text" placeholder="Optionstext">
+              </div>
             </mat-form-field>
-            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('gridRowStart')">
-              <mat-label>Startzeile</mat-label>
-              <input matInput type="number" [value]="combinedProperties.gridRowStart"
-                     (input)="updateModel('gridRowStart', $any($event.target).value)">
+
+            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('alignment')">
+              <mat-label>Ausrichtung</mat-label>
+              <mat-select [value]="combinedProperties.alignment"
+                          (change)="$any($event.target).value">
+                <mat-option *ngFor="let option of ['row', 'column']" [value]="option">
+                  {{option}}
+                </mat-option>
+              </mat-select>
             </mat-form-field>
-            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('gridRowEnd')">
-              <mat-label>Endzeile</mat-label>
-              <input matInput type="number" [value]="combinedProperties.gridRowEnd"
-                     (input)="updateModel('gridRowEnd', $any($event.target).value)">
+
+            <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('resizeEnabled')"
+                          [checked]="$any(combinedProperties.resizeEnabled)"
+                          (change)="updateModel('resizeEnabled', $event.checked)">
+              größenverstellbar
+            </mat-checkbox>
+
+            <mat-form-field disabled="true" *ngIf="combinedProperties.hasOwnProperty('sentences')">
+              <div *ngIf="combinedProperties.sentences !== undefined">
+                <mat-label>Sätze</mat-label>
+                <mat-list *ngFor="let sentence of $any(combinedProperties.sentences)">
+                  <mat-list-item>{{sentence}}</mat-list-item>
+                  <mat-divider></mat-divider>
+                </mat-list>
+              </div>
+              <div class="newOptionElement" fxLayout="row" fxLayoutAlign="center center">
+                <button mat-icon-button matPrefix
+                        (click)="updateModel('sentences', newOption.value)">
+                  <mat-icon>add</mat-icon>
+                </button>
+                <input #newOption matInput type="text" placeholder="Optionstext">
+              </div>
             </mat-form-field>
+
+          </div>
+          <button mat-raised-button class="delete-element-button" (click)="deleteElement()">
+            Element löschen
+          </button>
+          <button mat-raised-button class="duplicate-element-button" (click)="duplicateElement()">
+            Element duplizieren
+          </button>
+        </mat-tab>
+
+        <mat-tab>
+          <ng-template mat-tab-label>
+            <mat-icon class="example-tab-icon">format_shapes</mat-icon>
+          </ng-template>
+          <div fxLayout="column">
+
+            <ng-container *ngIf="!combinedProperties.dynamicPositioning; else elseBlock">
+              <mat-form-field *ngIf="combinedProperties.hasOwnProperty('width')">
+                <mat-label>Breite</mat-label>
+                <input matInput type="number" [value]="combinedProperties.width"
+                       (input)="updateModel('width', $any($event.target).value)">
+              </mat-form-field>
+              <mat-form-field *ngIf="combinedProperties.hasOwnProperty('height')">
+                <mat-label>Hoehe</mat-label>
+                <input matInput type="number" [value]="combinedProperties.height"
+                       (input)="updateModel('height', $any($event.target).value)">
+              </mat-form-field>
+              <mat-form-field *ngIf="combinedProperties.hasOwnProperty('xPosition')">
+                <mat-label>X Position</mat-label>
+                <input matInput type="number" [value]="combinedProperties.xPosition"
+                       (input)="updateModel('xPosition', $any($event.target).value)">
+              </mat-form-field>
+              <mat-form-field *ngIf="combinedProperties.hasOwnProperty('yPosition')">
+                <mat-label>Y Position</mat-label>
+                <input matInput type="number" [value]="combinedProperties.yPosition"
+                       (input)="updateModel('yPosition', $any($event.target).value)">
+              </mat-form-field>
+              <mat-form-field *ngIf="combinedProperties.hasOwnProperty('zIndex')">
+                <mat-label>Z-Index</mat-label>
+                <input matInput type="number" [value]="combinedProperties.zIndex"
+                       (input)="updateModel('zIndex', $any($event.target).value)"
+                       matTooltip="Priorität beim Stapeln von Elementen. Der höhere Index erscheint vorne.">
+              </mat-form-field>
+            </ng-container>
+            <ng-template #elseBlock>
+              <mat-form-field *ngIf="combinedProperties.hasOwnProperty('width')">
+                <mat-label>Mindestbreite</mat-label>
+                <input matInput type="number" [value]="combinedProperties.width"
+                       (input)="updateModel('width', $any($event.target).value)">
+              </mat-form-field>
+              <mat-form-field *ngIf="combinedProperties.hasOwnProperty('height')">
+                <mat-label>Mindesthoehe</mat-label>
+                <input matInput type="number" [value]="combinedProperties.height"
+                       (input)="updateModel('height', $any($event.target).value)">
+              </mat-form-field>
+
+              <mat-form-field *ngIf="combinedProperties.hasOwnProperty('gridColumnStart')">
+                <mat-label>Startspalte</mat-label>
+                <input matInput type="number" [value]="combinedProperties.gridColumnStart"
+                       (input)="updateModel('gridColumnStart', $any($event.target).value)">
+              </mat-form-field>
+              <mat-form-field *ngIf="combinedProperties.hasOwnProperty('gridColumnEnd')">
+                <mat-label>Endspalte</mat-label>
+                <input matInput type="number" [value]="combinedProperties.gridColumnEnd"
+                       (input)="updateModel('gridColumnEnd', $any($event.target).value)">
+              </mat-form-field>
+              <mat-form-field *ngIf="combinedProperties.hasOwnProperty('gridRowStart')">
+                <mat-label>Startzeile</mat-label>
+                <input matInput type="number" [value]="combinedProperties.gridRowStart"
+                       (input)="updateModel('gridRowStart', $any($event.target).value)">
+              </mat-form-field>
+              <mat-form-field *ngIf="combinedProperties.hasOwnProperty('gridRowEnd')">
+                <mat-label>Endzeile</mat-label>
+                <input matInput type="number" [value]="combinedProperties.gridRowEnd"
+                       (input)="updateModel('gridRowEnd', $any($event.target).value)">
+              </mat-form-field>
+            </ng-template>
+            <ng-container *ngIf="selectedElements.length > 1">
+              Ausrichtung
+              <div class="alignment-button-group" fxLayout="row" fxLayoutAlign="center center" >
+                <button (click)="alignElements('left')">
+                  <mat-icon>align_horizontal_left</mat-icon>
+                </button>
+                <button (click)="alignElements('right')">
+                  <mat-icon>align_horizontal_right</mat-icon>
+                </button>
+                <button (click)="alignElements('top')">
+                  <mat-icon>align_vertical_top</mat-icon>
+                </button>
+                <button (click)="alignElements('bottom')">
+                  <mat-icon>align_vertical_bottom</mat-icon>
+                </button>
+              </div>
+            </ng-container>
+          </div>
+        </mat-tab>
+
+        <mat-tab>
+          <ng-template mat-tab-label>
+            <mat-icon class="example-tab-icon">palette</mat-icon>
           </ng-template>
-          <ng-container *ngIf="selectedElements.length > 1">
-            Ausrichtung
-            <div class="alignment-button-group" fxLayout="row" fxLayoutAlign="center center" >
-              <button (click)="alignElements('left')">
-                <mat-icon>align_horizontal_left</mat-icon>
-              </button>
-              <button (click)="alignElements('right')">
-                <mat-icon>align_horizontal_right</mat-icon>
-              </button>
-              <button (click)="alignElements('top')">
-                <mat-icon>align_vertical_top</mat-icon>
-              </button>
-              <button (click)="alignElements('bottom')">
-                <mat-icon>align_vertical_bottom</mat-icon>
-              </button>
-            </div>
-          </ng-container>
-        </div>
-      </mat-tab>
-
-      <mat-tab>
-        <ng-template mat-tab-label>
-          <mat-icon class="example-tab-icon">palette</mat-icon>
-        </ng-template>
-        <div fxLayout="column">
-          <mat-form-field *ngIf="combinedProperties.hasOwnProperty('backgroundColor')"
-                          class="mdInput textsingleline">
-            <mat-label>Hintergrundfarbe</mat-label>
-            <input matInput type="text" [value]="combinedProperties.backgroundColor"
-                   (input)="updateModel('backgroundColor', $any($event.target).value)">
-          </mat-form-field>
-          <mat-form-field *ngIf="combinedProperties.hasOwnProperty('fontColor')"
-                          class="mdInput textsingleline">
-            <mat-label>Schriftfarbe</mat-label>
-            <input matInput type="text" [value]="combinedProperties.fontColor"
-                   (input)="updateModel('fontColor', $any($event.target).value)">
-          </mat-form-field>
-          <mat-form-field *ngIf="combinedProperties.hasOwnProperty('font')"
-                          class="mdInput textsingleline">
-            <mat-label>Schriftart</mat-label>
-            <input matInput type="text" [value]="combinedProperties.font"
-                   (input)="updateModel('font', $any($event.target).value)">
-          </mat-form-field>
-          <mat-form-field *ngIf="combinedProperties.hasOwnProperty('fontSize')"
-                          class="mdInput textsingleline">
-            <mat-label>Schriftgröße</mat-label>
-            <input matInput type="text" [value]="combinedProperties.fontSize"
-                   (input)="updateModel('fontSize', $any($event.target).value)">
-          </mat-form-field>
-
-          <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('bold')"
-                        [checked]="$any(combinedProperties.bold)"
-                        (change)="updateModel('bold', $event.checked)">
-            Fett
-          </mat-checkbox>
-          <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('italic')"
-                        [checked]="$any(combinedProperties.italic)"
-                        (change)="updateModel('italic', $event.checked)">
-            Kursiv
-          </mat-checkbox>
-          <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('underline')"
-                        [checked]="$any(combinedProperties.underline)"
-                        (change)="updateModel('underline', $event.checked)">
-            Unterstrichen
-          </mat-checkbox>
-        </div>
-      </mat-tab>
-    </mat-tab-group>
+          <div fxLayout="column">
+            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('backgroundColor')"
+                            class="mdInput textsingleline">
+              <mat-label>Hintergrundfarbe</mat-label>
+              <input matInput type="text" [value]="combinedProperties.backgroundColor"
+                     (input)="updateModel('backgroundColor', $any($event.target).value)">
+            </mat-form-field>
+            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('fontColor')"
+                            class="mdInput textsingleline">
+              <mat-label>Schriftfarbe</mat-label>
+              <input matInput type="text" [value]="combinedProperties.fontColor"
+                     (input)="updateModel('fontColor', $any($event.target).value)">
+            </mat-form-field>
+            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('font')"
+                            class="mdInput textsingleline">
+              <mat-label>Schriftart</mat-label>
+              <input matInput type="text" [value]="combinedProperties.font"
+                     (input)="updateModel('font', $any($event.target).value)">
+            </mat-form-field>
+            <mat-form-field *ngIf="combinedProperties.hasOwnProperty('fontSize')"
+                            class="mdInput textsingleline">
+              <mat-label>Schriftgröße</mat-label>
+              <input matInput type="text" [value]="combinedProperties.fontSize"
+                     (input)="updateModel('fontSize', $any($event.target).value)">
+            </mat-form-field>
+
+            <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('bold')"
+                          [checked]="$any(combinedProperties.bold)"
+                          (change)="updateModel('bold', $event.checked)">
+              Fett
+            </mat-checkbox>
+            <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('italic')"
+                          [checked]="$any(combinedProperties.italic)"
+                          (change)="updateModel('italic', $event.checked)">
+              Kursiv
+            </mat-checkbox>
+            <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('underline')"
+                          [checked]="$any(combinedProperties.underline)"
+                          (change)="updateModel('underline', $event.checked)">
+              Unterstrichen
+            </mat-checkbox>
+          </div>
+        </mat-tab>
+      </mat-tab-group>
+    </ng-container>
     `
 })
 export class ElementPropertiesComponent implements OnInit, OnDestroy {
-  @Input() selectedElements!: UnitUIElement[];
+  selectedElements!: UnitUIElement[];
   combinedProperties: Record<string, string | number | boolean | string[] | undefined> = {};
   private ngUnsubscribe = new Subject<void>();
 
-  constructor(public unitService: UnitService, private selectionService: SelectionService) { }
+  constructor(public unitService: UnitService) { }
 
   ngOnInit(): void {
     this.unitService.elementPropertyUpdated
@@ -315,7 +315,7 @@ export class ElementPropertiesComponent implements OnInit, OnDestroy {
           this.createCombinedProperties();
         }
       );
-    this.selectionService.elementSelected
+    this.unitService.elementSelected
       .pipe(takeUntil(this.ngUnsubscribe))
       .subscribe(
         (selectedElements: UnitUIElement[]) => {
@@ -350,7 +350,7 @@ export class ElementPropertiesComponent implements OnInit, OnDestroy {
   }
 
   alignElements(direction: 'left' | 'right' | 'top' | 'bottom'): void {
-    this.unitService.alignElements(this.selectionService.getSelectedElements(), direction);
+    this.unitService.alignSelectedElements(direction);
   }
 
   /* button group always handles values as string and since we also want to handle undefined
@@ -372,15 +372,11 @@ export class ElementPropertiesComponent implements OnInit, OnDestroy {
   }
 
   deleteElement(): void {
-    this.selectedElements.forEach((element: UnitUIElement) => {
-      this.unitService.deleteElement(element);
-    });
+    this.unitService.deleteSelectedElements();
   }
 
   duplicateElement(): void {
-    this.selectedElements.forEach((element: UnitUIElement) => {
-      this.unitService.duplicateElement(element);
-    });
+    this.unitService.duplicateSelectedElements();
   }
 
   ngOnDestroy(): void {
diff --git a/projects/editor/src/app/components/unit-view/page-view/properties/page-properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties/page-properties.component.ts
index f2de9aae3..730df013a 100644
--- a/projects/editor/src/app/components/unit-view/page-view/properties/page-properties.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/properties/page-properties.component.ts
@@ -1,7 +1,9 @@
-import { Component, Input } from '@angular/core';
+import { Component, Input, OnDestroy, OnInit } from '@angular/core';
 import { MatCheckboxChange } from '@angular/material/checkbox';
 import { UnitPage } from '../../../../../../../common/unit';
 import { UnitService } from '../../../../unit.service';
+import { takeUntil } from 'rxjs/operators';
+import { Subject } from 'rxjs';
 
 @Component({
   selector: 'app-page-properties',
@@ -26,14 +28,28 @@ import { UnitService } from '../../../../unit.service';
     </div>
     `
 })
-export class PagePropertiesComponent {
-  @Input() selectedPage!: UnitPage;
+export class PagePropertiesComponent implements OnInit, OnDestroy {
+  selectedPage!: UnitPage;
+  private ngUnsubscribe = new Subject<void>();
 
   constructor(private unitService: UnitService) { }
 
+  ngOnInit(): void {
+    this.unitService.selectedPage
+      .pipe(takeUntil(this.ngUnsubscribe))
+      .subscribe((page: UnitPage) => {
+        this.selectedPage = page;
+      });
+  }
+
   setPageAlwaysVisible(event: MatCheckboxChange): void {
     if (!this.unitService.setPageAlwaysVisible(event.checked)) {
       event.source.checked = false;
     }
   }
+
+  ngOnDestroy(): void {
+    this.ngUnsubscribe.next();
+    this.ngUnsubscribe.complete();
+  }
 }
diff --git a/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.html b/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.html
index f5c28f877..792f824ff 100644
--- a/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.html
+++ b/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.html
@@ -1,20 +1,20 @@
 <mat-accordion multi>
-  <mat-expansion-panel [expanded]="!expandElement">
+  <mat-expansion-panel [expanded]="!expandElementView">
     <mat-expansion-panel-header>
       <mat-panel-title>Seite</mat-panel-title>
     </mat-expansion-panel-header>
-      <app-page-properties [selectedPage]="selectedPage"></app-page-properties>
+      <app-page-properties></app-page-properties>
   </mat-expansion-panel>
   <mat-expansion-panel>
     <mat-expansion-panel-header>
       <mat-panel-title>Seitenabschnitt</mat-panel-title>
     </mat-expansion-panel-header>
-      <app-section-properties [selectedPageSection]="selectedPageSection"></app-section-properties>
+      <app-section-properties></app-section-properties>
   </mat-expansion-panel>
-  <mat-expansion-panel [expanded]="expandElement">
+  <mat-expansion-panel [expanded]="expandElementView">
     <mat-expansion-panel-header>
       <mat-panel-title>Element</mat-panel-title>
     </mat-expansion-panel-header>
-      <app-element-properties [selectedElements]="selectedElements"></app-element-properties>
+      <app-element-properties></app-element-properties>
   </mat-expansion-panel>
 </mat-accordion>
diff --git a/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.ts
index f260f5c9e..83c5b5573 100644
--- a/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.ts
@@ -3,7 +3,6 @@ import { Subject } from 'rxjs';
 import { takeUntil } from 'rxjs/operators';
 import { UnitService } from '../../../../unit.service';
 import { UnitPage, UnitPageSection, UnitUIElement } from '../../../../../../../common/unit';
-import { SelectionService } from '../canvas/selection.service';
 
 @Component({
   selector: 'app-properties',
@@ -21,26 +20,21 @@ export class PropertiesComponent {
   selectedElements: UnitUIElement[] = [];
   selectedPage!: UnitPage;
   selectedPageSection!: UnitPageSection;
-  expandElement: boolean = false;
+  expandElementView: boolean = false;
   private ngUnsubscribe = new Subject<void>();
 
-  constructor(public unitService: UnitService, private selectionService: SelectionService) { }
+  constructor(public unitService: UnitService) { }
 
   ngOnInit(): void {
     this.unitService.selectedPage
       .pipe(takeUntil(this.ngUnsubscribe))
       .subscribe((page: UnitPage) => {
         this.selectedPage = page;
-        this.expandElement = false;
+        this.expandElementView = false;
       });
-    this.unitService.selectedPageSection
+    this.unitService.elementSelected
       .pipe(takeUntil(this.ngUnsubscribe))
-      .subscribe((pageSection: UnitPageSection) => {
-        this.selectedPageSection = pageSection;
-      });
-    this.selectionService.elementSelected
-      .pipe(takeUntil(this.ngUnsubscribe))
-      .subscribe(() => { this.expandElement = true; });
+      .subscribe(() => { this.expandElementView = true; });
   }
 
   ngOnDestroy(): void {
diff --git a/projects/editor/src/app/components/unit-view/page-view/properties/section-properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties/section-properties.component.ts
index 8d36bcb2c..34e3d16a9 100644
--- a/projects/editor/src/app/components/unit-view/page-view/properties/section-properties.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/properties/section-properties.component.ts
@@ -1,11 +1,15 @@
-import { Component, Input } from '@angular/core';
+import {
+  Component, Input, OnDestroy, OnInit
+} from '@angular/core';
+import { Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
 import { UnitPageSection } from '../../../../../../../common/unit';
 import { UnitService } from '../../../../unit.service';
 
 @Component({
   selector: 'app-section-properties',
   template: `
-    <div fxLayout="column">
+    <div *ngIf="selectedPageSection" fxLayout="column">
       <mat-form-field>
         <mat-label>Breite</mat-label>
         <input matInput type="number" [(ngModel)]="selectedPageSection.width">
@@ -19,7 +23,7 @@ import { UnitService } from '../../../../unit.service';
         <input matInput type="text" [(ngModel)]="selectedPageSection.backgroundColor">
       </mat-form-field>
       <mat-checkbox [checked]="selectedPageSection.dynamicPositioning"
-                    (change)="unitService.setSectionDynamicPositioning(selectedPageSection, $event.checked)">
+                    (change)="unitService.setSectionDynamicPositioning($event.checked)">
         dynamisches Layout
       </mat-checkbox>
       <mat-form-field>
@@ -28,14 +32,28 @@ import { UnitService } from '../../../../unit.service';
       </mat-form-field>
     </div>
     <button mat-raised-button
-            (click)="unitService.deleteSection()">
+            (click)="unitService.deleteSelectedSection()">
       Entfernen
       <mat-icon>remove</mat-icon>
     </button>
   `
 })
-export class SectionPropertiesComponent {
-  @Input() selectedPageSection!: UnitPageSection;
+export class SectionPropertiesComponent implements OnInit, OnDestroy {
+  selectedPageSection!: UnitPageSection;
+  private ngUnsubscribe = new Subject<void>();
 
   constructor(public unitService: UnitService) { }
+
+  ngOnInit(): void {
+    this.unitService.selectedPageSection
+      .pipe(takeUntil(this.ngUnsubscribe))
+      .subscribe((pageSection: UnitPageSection) => {
+        this.selectedPageSection = pageSection;
+      });
+  }
+
+  ngOnDestroy(): void {
+    this.ngUnsubscribe.next();
+    this.ngUnsubscribe.complete();
+  }
 }
diff --git a/projects/editor/src/app/components/unit-view/unit-view.component.html b/projects/editor/src/app/components/unit-view/unit-view.component.html
index bd0a362f0..655cd2cc2 100644
--- a/projects/editor/src/app/components/unit-view/unit-view.component.html
+++ b/projects/editor/src/app/components/unit-view/unit-view.component.html
@@ -22,7 +22,7 @@
               <mat-icon>delete</mat-icon>
             </button>
           </ng-template>
-          <app-page-view [pageIndex]="i"></app-page-view>
+          <app-page-view [page]="page"></app-page-view>
         </mat-tab>
         <mat-tab disabled>
           <ng-template mat-tab-label>
diff --git a/projects/editor/src/app/unit.service.ts b/projects/editor/src/app/unit.service.ts
index b00dc3d10..014a399bd 100644
--- a/projects/editor/src/app/unit.service.ts
+++ b/projects/editor/src/app/unit.service.ts
@@ -8,6 +8,10 @@ import * as UnitFactory from './model/UnitFactory';
 import { MessageService } from '../../../common/message.service';
 import { IdService } from './id.service';
 import { DialogService } from './dialog.service';
+// eslint-disable-next-line import/no-cycle
+import { CanvasElementOverlay } from './components/unit-view/page-view/canvas/canvas-element-overlay';
+// eslint-disable-next-line import/no-cycle
+import { CanvasSectionComponent } from './components/unit-view/page-view/canvas/canvas-section.component';
 
 @Injectable({
   providedIn: 'root'
@@ -16,13 +20,14 @@ export class UnitService {
   private _unit: BehaviorSubject<Unit>;
   private _selectedPage: BehaviorSubject<UnitPage>;
   private _selectedPageSection: BehaviorSubject<UnitPageSection>;
+  private selectedPageSectionComponent!: CanvasSectionComponent;
   private _selectedPageIndex: BehaviorSubject<number>; // TODO weg refactorn
-  private _pages: BehaviorSubject<UnitPage>[];
-
-  private _selectedPageSectionIndex: BehaviorSubject<number>;
 
   elementPropertyUpdated: Subject<void> = new Subject<void>();
 
+  selectedComponentElements: CanvasElementOverlay[] = [];
+  elementSelected: Subject<UnitUIElement[]> = new Subject<UnitUIElement[]>();
+
   constructor(private messageService: MessageService,
               private idService: IdService,
               private dialogService: DialogService) {
@@ -33,14 +38,38 @@ export class UnitService {
     initialUnit.pages.push(initialPage);
 
     this._unit = new BehaviorSubject(initialUnit);
-    this._pages = [new BehaviorSubject(initialPage as UnitPage)];
     this._selectedPageIndex = new BehaviorSubject(0);
     this._selectedPage = new BehaviorSubject(initialPage);
     this._selectedPageSection = new BehaviorSubject(initialPage.sections[0]);
+  }
 
-    this._selectedPageSectionIndex = new BehaviorSubject<number>(0);
+  // == SELECTION ===============================
+  selectElement(event: { componentElement: CanvasElementOverlay; multiSelect: boolean }): void {
+    if (!event.multiSelect) {
+      this.clearSelection();
+    }
+    this.selectedComponentElements.push(event.componentElement);
+    event.componentElement.setSelected(true); // TODO direkt in der component?
+    this.elementSelected.next(this.selectedComponentElements.map(componentElement => componentElement.element));
+  }
+
+  private clearSelection() {
+    this.selectedComponentElements.forEach((overlayComponent: CanvasElementOverlay) => {
+      overlayComponent.setSelected(false);
+    });
+    this.selectedComponentElements = [];
   }
 
+  selectSection(sectionComponent: CanvasSectionComponent): void {
+    if (this.selectedPageSectionComponent) {
+      this.selectedPageSectionComponent.selected = false;
+    }
+    this.selectedPageSectionComponent = sectionComponent;
+    this.selectedPageSectionComponent.selected = true;
+    this._selectedPageSection.next(sectionComponent.section);
+  }
+  // ===========================================
+
   get unit(): Observable<Unit> {
     return this._unit.asObservable();
   }
@@ -57,23 +86,10 @@ export class UnitService {
     return this._selectedPageIndex.asObservable();
   }
 
-  get selectedPageSectionIndex(): Observable<number> {
-    return this._selectedPageSectionIndex.asObservable();
-  }
-
-  getSelectedPageSection(): UnitPageSection {
-    return this._unit.value.pages[this._selectedPageIndex.value].sections[this._selectedPageSectionIndex.value];
-  }
-
-  getPageObservable(index: number): Observable<UnitPage> {
-    return this._pages[index].asObservable();
-  }
-
   addPage(): void {
     const newPage = UnitFactory.createUnitPage(this._unit.value.pages.length);
     newPage.sections.push(UnitFactory.createUnitPageSection());
     this._unit.value.pages.push(newPage);
-    this._pages.push(new BehaviorSubject(newPage as UnitPage));
 
     this._unit.next(this._unit.value);
     this._selectedPageIndex.next(this._unit.value.pages.length - 1);
@@ -82,8 +98,6 @@ export class UnitService {
 
   deletePage(index: number = this._selectedPageIndex.value): void {
     this._unit.value.pages.splice(index, 1);
-    this._pages.splice(index, 1);
-
     this._unit.next(this._unit.value);
     if (index === this._selectedPageIndex.value) {
       this._selectedPageIndex.next(this._selectedPageIndex.value - 1);
@@ -102,24 +116,18 @@ export class UnitService {
   }
 
   addSection(): void {
-    const newSection = UnitFactory.createUnitPageSection();
-    this._unit.value.pages[this._selectedPageIndex.value].sections.push(newSection);
+    this._unit.value.pages[this._selectedPageIndex.value].sections.push(UnitFactory.createUnitPageSection());
     this._unit.next(this._unit.value);
-    this._pages[this._selectedPageIndex.value].next(this._unit.value.pages[this._selectedPageIndex.value]); // TODO auslagern?
   }
 
-  deleteSection(): void {
+  deleteSelectedSection(): void {
     if (this._unit.value.pages[this._selectedPageIndex.value].sections.length < 2) {
       this.messageService.showWarning('cant delete last section');
     } else {
-      const index = this._selectedPageSectionIndex.value;
-      this._unit.value.pages[this._selectedPageIndex.value].sections.splice(index, 1);
+      this._unit.value.pages[this._selectedPageIndex.value].sections.splice(
+        this._unit.value.pages[this._selectedPageIndex.value].sections.indexOf(this._selectedPageSection.value), 1
+      );
       this._unit.next(this._unit.value);
-
-      this._pages[this._selectedPageIndex.value].next(this._unit.value.pages[this._selectedPageIndex.value]);
-      if (this._selectedPageSectionIndex.value > 0) {
-        this._selectedPageSectionIndex.next(this._selectedPageSectionIndex.value - 1);
-      }
     }
   }
 
@@ -176,32 +184,25 @@ export class UnitService {
         throw new Error(`ElementType ${elementType} not found!`);
     }
     newElement.id = this.idService.getNewID(elementType);
-    newElement.dynamicPositioning = this._unit.value.pages[this._selectedPageIndex.value]
-      .sections[this._selectedPageSectionIndex.value].dynamicPositioning;
-    this._unit.value.pages[this._selectedPageIndex.value]
-      .sections[this._selectedPageSectionIndex.value].elements.push(newElement!);
-
-    this._pages[this._selectedPageIndex.value].next(this._unit.value.pages[this._selectedPageIndex.value]);
+    newElement.dynamicPositioning = this._selectedPageSection.value.dynamicPositioning;
+    this._selectedPageSection.value.elements.push(newElement);
   }
 
-  deleteElement(elementToDelete: UnitUIElement): void {
-    const oldElements = this._unit.value.pages[this._selectedPageIndex.value]
-      .sections[this._selectedPageSectionIndex.value].elements;
-    this._unit.value.pages[this._selectedPageIndex.value]
-      .sections[this._selectedPageSectionIndex.value].elements =
-      oldElements.filter(element => element !== elementToDelete);
-    this._pages[this._selectedPageIndex.value].next(this._unit.value.pages[this._selectedPageIndex.value]);
+  deleteSelectedElements(): void {
+    const selectedElements = this.selectedComponentElements.map(componentElement => componentElement.element);
+    this._selectedPageSection.value.elements =
+      this._selectedPageSection.value.elements.filter(element => !selectedElements.includes(element));
   }
 
-  duplicateElement(elementToDuplicate: UnitUIElement): void {
-    const newElement: UnitUIElement = { ...elementToDuplicate };
-    newElement.id = this.idService.getNewID(newElement.type);
-    newElement.xPosition += 10;
-    newElement.yPosition += 10;
-
-    this._unit.value.pages[this._selectedPageIndex.value]
-      .sections[this._selectedPageSectionIndex.value].elements.push(newElement);
-    this._pages[this._selectedPageIndex.value].next(this._unit.value.pages[this._selectedPageIndex.value]);
+  duplicateSelectedElements(): void {
+    const selectedElements = this.selectedComponentElements.map(componentElement => componentElement.element);
+    selectedElements.forEach((element: UnitUIElement) => {
+      const newElement: UnitUIElement = { ...element };
+      newElement.id = this.idService.getNewID(newElement.type);
+      newElement.xPosition += 10;
+      newElement.yPosition += 10;
+      this._selectedPageSection.value.elements.push(newElement);
+    });
   }
 
   updatePageSelection(newIndex: number): void {
@@ -209,11 +210,6 @@ export class UnitService {
     this._selectedPage.next(this._unit.value.pages[newIndex]);
   }
 
-  updatePageSectionSelection(newIndex: number): void {
-    this._selectedPageSectionIndex.next(newIndex);
-    this._selectedPageSection.next(this._unit.value.pages[this._selectedPageIndex.value].sections[newIndex]);
-  }
-
   updateElementProperty(
     element: UnitUIElement, property: string, value: string | number | boolean | undefined
   ): boolean {
@@ -236,7 +232,8 @@ export class UnitService {
     return true;
   }
 
-  alignElements(elements: UnitUIElement[], alignmentDirection: 'left' | 'right' | 'top' | 'bottom'): void {
+  alignSelectedElements(alignmentDirection: 'left' | 'right' | 'top' | 'bottom'): void {
+    const elements = this.selectedComponentElements.map(componentElement => componentElement.element);
     let newValue: number;
     switch (alignmentDirection) {
       case 'left':
@@ -268,7 +265,8 @@ export class UnitService {
     this.elementPropertyUpdated.next();
   }
 
-  setSectionDynamicPositioning(section: UnitPageSection, value: boolean): void {
+  setSectionDynamicPositioning(value: boolean): void {
+    const section = this._selectedPageSection.value;
     section.dynamicPositioning = value;
     section.elements.forEach((element: UnitUIElement) => {
       element.dynamicPositioning = value;
@@ -286,11 +284,6 @@ export class UnitService {
     this._selectedPageIndex.next(0);
     this._selectedPage.next(this._unit.value.pages[0]);
     this._unit.next(newUnit);
-    this._pages = [];
-    this._unit.value.pages.forEach((page: UnitPage) => {
-      this._pages.push(new BehaviorSubject(page));
-    });
-
     this.idService.readExistingIDs(this._unit.value);
   }
 
-- 
GitLab