From eaaa07306c6fd64613e5ba64eaeea9462e9465c2 Mon Sep 17 00:00:00 2001
From: rhenck <richard.henck@iqb.hu-berlin.de>
Date: Thu, 7 Oct 2021 17:37:24 +0200
Subject: [PATCH] [editor] Refactor properties panel

The different tabs are now separate components, which makes them a lot
smaller and better to handle.
---
 projects/editor/src/app/app.module.ts         |   6 +-
 .../element-properties.component.html         | 178 +-----------------
 .../element-properties.component.ts           |   4 -
 .../element-sizing-properties.component.ts    | 157 +++++++++++++++
 .../element-style-properties.component.ts     |  55 ++++++
 5 files changed, 223 insertions(+), 177 deletions(-)
 create mode 100644 projects/editor/src/app/components/unit-view/page-view/properties/element-sizing-properties.component.ts
 create mode 100644 projects/editor/src/app/components/unit-view/page-view/properties/element-style-properties.component.ts

diff --git a/projects/editor/src/app/app.module.ts b/projects/editor/src/app/app.module.ts
index 9a49daf71..3163f7763 100644
--- a/projects/editor/src/app/app.module.ts
+++ b/projects/editor/src/app/app.module.ts
@@ -29,6 +29,8 @@ import { SectionMenuComponent } from './components/unit-view/page-view/canvas/se
 import { SectionStaticComponent } from './components/unit-view/page-view/canvas/section-static.component';
 import { SectionDynamicComponent } from './components/unit-view/page-view/canvas/section-dynamic.component';
 import { RichTextEditorComponent } from './components/unit-view/page-view/rich-text-editor.component';
+import { ElementStylePropertiesComponent } from './components/unit-view/page-view/properties/element-style-properties.component';
+import { ElementSizingPropertiesComponent } from './components/unit-view/page-view/properties/element-sizing-properties.component';
 
 @NgModule({
   declarations: [
@@ -48,7 +50,9 @@ import { RichTextEditorComponent } from './components/unit-view/page-view/rich-t
     SectionMenuComponent,
     SectionStaticComponent,
     SectionDynamicComponent,
-    RichTextEditorComponent
+    RichTextEditorComponent,
+    ElementStylePropertiesComponent,
+    ElementSizingPropertiesComponent
   ],
   imports: [
     BrowserModule,
diff --git a/projects/editor/src/app/components/unit-view/page-view/properties/element-properties.component.html b/projects/editor/src/app/components/unit-view/page-view/properties/element-properties.component.html
index 7d8f984db..8ef8bba42 100644
--- a/projects/editor/src/app/components/unit-view/page-view/properties/element-properties.component.html
+++ b/projects/editor/src/app/components/unit-view/page-view/properties/element-properties.component.html
@@ -218,184 +218,18 @@
       <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.width" appearance="fill">
-            <mat-label>Breite</mat-label>
-            <input matInput type="number" #width="ngModel" min="0"
-                   [ngModel]="combinedProperties.width"
-                   (ngModelChange)="updateModel('width', $event, width.valid)">
-          </mat-form-field>
-          <mat-form-field *ngIf="combinedProperties.height" appearance="fill">
-            <mat-label>Hoehe</mat-label>
-            <input matInput type="number" #height="ngModel" min="0"
-                   [ngModel]="combinedProperties.height"
-                   (ngModelChange)="updateModel('height', $event, height.valid)">
-          </mat-form-field>
-
-          <mat-form-field *ngIf="combinedProperties.xPosition" appearance="fill">
-            <mat-label>X Position</mat-label>
-            <input matInput type="number" #xPosition="ngModel" min="0"
-                   [ngModel]="combinedProperties.xPosition"
-                   (ngModelChange)="updateModel('xPosition', $event, xPosition.valid)">
-          </mat-form-field>
-
-          <mat-form-field *ngIf="combinedProperties.yPosition" appearance="fill">
-            <mat-label>Y Position</mat-label>
-            <input matInput type="number" #yPosition="ngModel" min="0"
-                   [ngModel]="combinedProperties.yPosition"
-                   (ngModelChange)="updateModel('yPosition', $event, yPosition.valid)">
-          </mat-form-field>
-        </ng-container>
-        <ng-template #elseBlock>
-          <mat-form-field *ngIf="combinedProperties.width" appearance="fill">
-            <mat-label>Mindestbreite</mat-label>
-            <input matInput type="number" #width="ngModel" min="0"
-                   [ngModel]="combinedProperties.width"
-                   (ngModelChange)="updateModel('width', $event, width.valid)">
-          </mat-form-field>
-          <mat-form-field *ngIf="combinedProperties.height" appearance="fill">
-            <mat-label>Mindesthöhe</mat-label>
-            <input matInput type="number" #height="ngModel" min="0"
-                   [ngModel]="combinedProperties.height"
-                   (ngModelChange)="updateModel('height', $event, height.valid)">
-          </mat-form-field>
-          Grid
-          <div class="input-group">
-            <div fxLayoutAlign="row">
-              <mat-form-field *ngIf="combinedProperties.gridColumnStart" class="small-input">
-                <mat-label>Start-Spalte</mat-label>
-                <input matInput type="number" [ngModel]="combinedProperties.gridColumnStart"
-                       (ngModelChange)="updateModel('gridColumnStart', $event)">
-              </mat-form-field>
-              <mat-form-field *ngIf="combinedProperties.gridColumnEnd" class="small-input">
-                <mat-label>End-Spalte</mat-label>
-                <input matInput type="number" [ngModel]="combinedProperties.gridColumnEnd"
-                       (ngModelChange)="updateModel('gridColumnEnd', $event)">
-              </mat-form-field>
-            </div>
-            <div fxLayoutAlign="row">
-              <mat-form-field *ngIf="combinedProperties.gridRowStart" class="small-input">
-                <mat-label>Start-Zeile</mat-label>
-                <input matInput type="number" [ngModel]="combinedProperties.gridRowStart"
-                       (ngModelChange)="updateModel('gridRowStart', $event)">
-              </mat-form-field>
-              <mat-form-field *ngIf="combinedProperties.gridRowEnd" class="small-input">
-                <mat-label>End-Zeile</mat-label>
-                <input matInput type="number" [ngModel]="combinedProperties.gridRowEnd"
-                       (ngModelChange)="updateModel('gridRowEnd', $event)">
-              </mat-form-field>
-            </div>
-          </div>
-
-          Abstand
-          <div class="input-group">
-            <mat-form-field *ngIf="combinedProperties.marginTop"
-                            class="centered-form-field">
-              <mat-label>oben</mat-label>
-              <input matInput type="number" #marginTop="ngModel" min="0"
-                     [ngModel]="combinedProperties.marginTop"
-                     (ngModelChange)="updateModel('marginTop', $event, marginTop.valid)">
-            </mat-form-field>
-            <div fxLayoutAlign="row">
-              <mat-form-field *ngIf="combinedProperties.marginLeft">
-                <mat-label>links</mat-label>
-                <input matInput type="number" #marginLeft="ngModel" min="0"
-                       [ngModel]="combinedProperties.marginLeft"
-                       (ngModelChange)="updateModel('marginLeft', $event, marginLeft.valid)">
-              </mat-form-field>
-              <mat-form-field *ngIf="combinedProperties.marginRight"
-                              class="right-form-field">
-                <mat-label>rechts</mat-label>
-                <input matInput type="number" #marginRight="ngModel" min="0"
-                       [ngModel]="combinedProperties.marginRight"
-                       (ngModelChange)="updateModel('marginRight', $event, marginRight.valid)">
-              </mat-form-field>
-            </div>
-            <mat-form-field *ngIf="combinedProperties.marginBottom"
-                            class="centered-form-field">
-              <mat-label>unten</mat-label>
-              <input matInput type="number" #marginBottom="ngModel" min="0"
-                     [ngModel]="combinedProperties.marginBottom"
-                     (ngModelChange)="updateModel('marginBottom', $event, marginBottom.valid)">
-            </mat-form-field>
-          </div>
-        </ng-template>
-
-        <mat-form-field *ngIf="combinedProperties.zIndex" appearance="fill">
-          <mat-label>Z-Index</mat-label>
-          <input matInput type="number" #zIndex="ngModel" min="0"
-                 [ngModel]="combinedProperties.zIndex"
-                 (ngModelChange)="updateModel('zIndex', $event, zIndex.valid)"
-                 matTooltip="Priorität beim Stapeln von Elementen. Der höhere Index erscheint vorne.">
-        </mat-form-field>
-        <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>
+        <app-element-sizing-properties [combinedProperties]="combinedProperties"
+                                       (updateModel)="updateModel($event.property, $event.value, $event.isInputValid)">
+        </app-element-sizing-properties>
     </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.backgroundColor"
-                        appearance="fill" class="mdInput textsingleline">
-          <mat-label>Hintergrundfarbe</mat-label>
-          <input matInput type="color" [value]="combinedProperties.backgroundColor"
-                 (input)="updateModel('backgroundColor', $any($event.target).value)">
-        </mat-form-field>
-        <mat-form-field *ngIf="combinedProperties.fontColor"
-                        appearance="fill" class="mdInput textsingleline">
-          <mat-label>Schriftfarbe</mat-label>
-          <input matInput type="color" [value]="combinedProperties.fontColor"
-                 (input)="updateModel('fontColor', $any($event.target).value)">
-        </mat-form-field>
-        <mat-form-field *ngIf="combinedProperties.font"
-                        appearance="fill" 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.fontSize"
-                        appearance="fill" 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.bold"
-                      [checked]="$any(combinedProperties.bold)"
-                      (change)="updateModel('bold', $event.checked)">
-          Fett
-        </mat-checkbox>
-        <mat-checkbox *ngIf="combinedProperties.italic"
-                      [checked]="$any(combinedProperties.italic)"
-                      (change)="updateModel('italic', $event.checked)">
-          Kursiv
-        </mat-checkbox>
-        <mat-checkbox *ngIf="combinedProperties.underline"
-                      [checked]="$any(combinedProperties.underline)"
-                      (change)="updateModel('underline', $event.checked)">
-          Unterstrichen
-        </mat-checkbox>
-      </div>
+        <app-element-style-properties [combinedProperties]="combinedProperties"
+          (updateModel)="updateModel($event.property, $event.value)">
+        </app-element-style-properties>
     </mat-tab>
   </mat-tab-group>
 </ng-container>
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 77dcd8935..f1f43c1bf 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
@@ -75,10 +75,6 @@ export class ElementPropertiesComponent implements OnInit, OnDestroy {
     }
   }
 
-  alignElements(direction: 'left' | 'right' | 'top' | 'bottom'): void {
-    this.unitService.alignElements(this.selectionService.getSelectedElements(), direction);
-  }
-
   deleteElement(): void {
     this.unitService.deleteElementsFromSectionByIndex(
       this.selectedElements,
diff --git a/projects/editor/src/app/components/unit-view/page-view/properties/element-sizing-properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties/element-sizing-properties.component.ts
new file mode 100644
index 000000000..b3dc0807e
--- /dev/null
+++ b/projects/editor/src/app/components/unit-view/page-view/properties/element-sizing-properties.component.ts
@@ -0,0 +1,157 @@
+import {
+  Component, Input, Output, EventEmitter
+} from '@angular/core';
+import { UnitService } from '../../../../unit.service';
+import { SelectionService } from '../../../../selection.service';
+
+@Component({
+  selector: 'app-element-sizing-properties',
+  template: `
+    <div fxLayout="column">
+      <ng-container *ngIf="!combinedProperties.dynamicPositioning; else elseBlock">
+        <mat-form-field *ngIf="combinedProperties.width" appearance="fill">
+          <mat-label>Breite</mat-label>
+          <input matInput type="number" #width="ngModel" min="0"
+                 [ngModel]="combinedProperties.width"
+                 (ngModelChange)="updateModel.emit({ property: 'width', value: $event, isInputValid: width.valid })">
+        </mat-form-field>
+        <mat-form-field *ngIf="combinedProperties.height" appearance="fill">
+          <mat-label>Hoehe</mat-label>
+          <input matInput type="number" #height="ngModel" min="0"
+                 [ngModel]="combinedProperties.height"
+                 (ngModelChange)="updateModel.emit({ property: 'height', value: $event, isInputValid: height.valid })">
+        </mat-form-field>
+
+        <mat-form-field *ngIf="combinedProperties.xPosition" appearance="fill">
+          <mat-label>X Position</mat-label>
+          <input matInput type="number" #xPosition="ngModel" min="0"
+                 [ngModel]="combinedProperties.xPosition"
+                 (ngModelChange)="updateModel.emit(
+                    { property: 'xPosition', value: $event, isInputValid: xPosition.valid })">
+        </mat-form-field>
+
+        <mat-form-field *ngIf="combinedProperties.yPosition" appearance="fill">
+          <mat-label>Y Position</mat-label>
+          <input matInput type="number" #yPosition="ngModel" min="0"
+                 [ngModel]="combinedProperties.yPosition"
+                 (ngModelChange)="updateModel.emit(
+                    { property: 'yPosition', value: $event, isInputValid: yPosition.valid })">
+        </mat-form-field>
+      </ng-container>
+      <ng-template #elseBlock>
+        <mat-form-field *ngIf="combinedProperties.width" appearance="fill">
+          <mat-label>Mindestbreite</mat-label>
+          <input matInput type="number" #width="ngModel" min="0"
+                 [ngModel]="combinedProperties.width"
+                 (ngModelChange)="updateModel.emit({ property: 'width', value: $event, isInputValid: width.valid })">
+        </mat-form-field>
+        <mat-form-field *ngIf="combinedProperties.height" appearance="fill">
+          <mat-label>Mindesthöhe</mat-label>
+          <input matInput type="number" #height="ngModel" min="0"
+                 [ngModel]="combinedProperties.height"
+                 (ngModelChange)="updateModel.emit({ property: 'height', value: $event, isInputValid: height.valid })">
+        </mat-form-field>
+        Grid
+        <div class="input-group">
+          <div fxLayoutAlign="row">
+            <mat-form-field *ngIf="combinedProperties.gridColumnStart" class="small-input">
+              <mat-label>Start-Spalte</mat-label>
+              <input matInput type="number" [ngModel]="combinedProperties.gridColumnStart"
+                     (ngModelChange)="updateModel.emit({ property: 'gridColumnStart', value: $event })">
+            </mat-form-field>
+            <mat-form-field *ngIf="combinedProperties.gridColumnEnd" class="small-input">
+              <mat-label>End-Spalte</mat-label>
+              <input matInput type="number" [ngModel]="combinedProperties.gridColumnEnd"
+                     (ngModelChange)="updateModel.emit({ property: 'gridColumnEnd', value: $event })">
+            </mat-form-field>
+          </div>
+          <div fxLayoutAlign="row">
+            <mat-form-field *ngIf="combinedProperties.gridRowStart" class="small-input">
+              <mat-label>Start-Zeile</mat-label>
+              <input matInput type="number" [ngModel]="combinedProperties.gridRowStart"
+                     (ngModelChange)="updateModel.emit({ property: 'gridRowStart', value: $event })">
+            </mat-form-field>
+            <mat-form-field *ngIf="combinedProperties.gridRowEnd" class="small-input">
+              <mat-label>End-Zeile</mat-label>
+              <input matInput type="number" [ngModel]="combinedProperties.gridRowEnd"
+                     (ngModelChange)="updateModel.emit({ property: 'gridRowEnd', value: $event })">
+            </mat-form-field>
+          </div>
+        </div>
+
+        Abstand
+        <div class="input-group">
+          <mat-form-field *ngIf="combinedProperties.marginTop"
+                          class="centered-form-field">
+            <mat-label>oben</mat-label>
+            <input matInput type="number" #marginTop="ngModel" min="0"
+                   [ngModel]="combinedProperties.marginTop"
+                   (ngModelChange)="updateModel.emit(
+                      { property: 'marginTop', value: $event, isInputValid: marginTop.valid })">
+          </mat-form-field>
+          <div fxLayoutAlign="row">
+            <mat-form-field *ngIf="combinedProperties.marginLeft">
+              <mat-label>links</mat-label>
+              <input matInput type="number" #marginLeft="ngModel" min="0"
+                     [ngModel]="combinedProperties.marginLeft"
+                     (ngModelChange)="updateModel.emit(
+                        { property: 'marginLeft', value: $event, isInputValid: marginLeft.valid })">
+            </mat-form-field>
+            <mat-form-field *ngIf="combinedProperties.marginRight"
+                            class="right-form-field">
+              <mat-label>rechts</mat-label>
+              <input matInput type="number" #marginRight="ngModel" min="0"
+                     [ngModel]="combinedProperties.marginRight"
+                     (ngModelChange)="updateModel.emit(
+                        { property: 'marginRight', value: $event, isInputValid: marginRight.valid })">
+            </mat-form-field>
+          </div>
+          <mat-form-field *ngIf="combinedProperties.marginBottom"
+                          class="centered-form-field">
+            <mat-label>unten</mat-label>
+            <input matInput type="number" #marginBottom="ngModel" min="0"
+                   [ngModel]="combinedProperties.marginBottom"
+                   (ngModelChange)="updateModel.emit(
+                      { property: 'marginBottom', value: $event, isInputValid: marginBottom.valid })">
+          </mat-form-field>
+        </div>
+      </ng-template>
+
+      <mat-form-field *ngIf="combinedProperties.zIndex" appearance="fill">
+        <mat-label>Z-Index</mat-label>
+        <input matInput type="number" #zIndex="ngModel" min="0"
+               [ngModel]="combinedProperties.zIndex"
+               (ngModelChange)="updateModel.emit({ property: 'zIndex', value: $event, isInputValid: zIndex.valid })"
+               matTooltip="Priorität beim Stapeln von Elementen. Der höhere Index erscheint vorne.">
+      </mat-form-field>
+      <ng-container *ngIf="(selectionService.selectedElements | async)!.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>
+  `
+})
+export class ElementSizingPropertiesComponent {
+  @Input() combinedProperties: Record<string, string | number | boolean | string[] | undefined> = {};
+  @Output() updateModel =
+  new EventEmitter<{ property: string; value: string | boolean, isInputValid?: boolean | null }>();
+
+  constructor(private unitService: UnitService, public selectionService: SelectionService) { }
+
+  alignElements(direction: 'left' | 'right' | 'top' | 'bottom'): void {
+    this.unitService.alignElements(this.selectionService.getSelectedElements(), direction);
+  }
+}
diff --git a/projects/editor/src/app/components/unit-view/page-view/properties/element-style-properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties/element-style-properties.component.ts
new file mode 100644
index 000000000..013193f7d
--- /dev/null
+++ b/projects/editor/src/app/components/unit-view/page-view/properties/element-style-properties.component.ts
@@ -0,0 +1,55 @@
+import {
+  Component, EventEmitter, Input, Output
+} from '@angular/core';
+
+@Component({
+  selector: 'app-element-style-properties',
+  template: `
+    <div fxLayout="column">
+      <mat-form-field *ngIf="combinedProperties.backgroundColor"
+                      appearance="fill" class="mdInput textsingleline">
+        <mat-label>Hintergrundfarbe</mat-label>
+        <input matInput type="color" [value]="combinedProperties.backgroundColor"
+               (input)="updateModel.emit({ property: 'backgroundColor', value: $any($event.target).value })">
+      </mat-form-field>
+      <mat-form-field *ngIf="combinedProperties.fontColor"
+                      appearance="fill" class="mdInput textsingleline">
+        <mat-label>Schriftfarbe</mat-label>
+        <input matInput type="color" [value]="combinedProperties.fontColor"
+               (input)="updateModel.emit({ property: 'fontColor', value: $any($event.target).value })">
+      </mat-form-field>
+      <mat-form-field *ngIf="combinedProperties.font"
+                      appearance="fill" class="mdInput textsingleline">
+        <mat-label>Schriftart</mat-label>
+        <input matInput type="text" [value]="combinedProperties.font"
+               (input)="updateModel.emit({ property: 'font', value: $any($event.target).value })">
+      </mat-form-field>
+      <mat-form-field *ngIf="combinedProperties.fontSize"
+                      appearance="fill" class="mdInput textsingleline">
+        <mat-label>Schriftgröße</mat-label>
+        <input matInput type="text" [value]="combinedProperties.fontSize"
+               (input)="updateModel.emit({ property: 'fontSize', value: $any($event.target).value })">
+      </mat-form-field>
+
+      <mat-checkbox *ngIf="combinedProperties.bold"
+                    [checked]="$any(combinedProperties.bold)"
+                    (change)="updateModel.emit({ property: 'bold', value: $event.checked })">
+        Fett
+      </mat-checkbox>
+      <mat-checkbox *ngIf="combinedProperties.italic"
+                    [checked]="$any(combinedProperties.italic)"
+                    (change)="updateModel.emit({ property: 'italic', value: $event.checked })">
+        Kursiv
+      </mat-checkbox>
+      <mat-checkbox *ngIf="combinedProperties.underline"
+                    [checked]="$any(combinedProperties.underline)"
+                    (change)="updateModel.emit({ property: 'underline', value: $event.checked })">
+        Unterstrichen
+      </mat-checkbox>
+    </div>
+  `
+})
+export class ElementStylePropertiesComponent {
+  @Input() combinedProperties: Record<string, string | number | boolean | string[] | undefined> = {};
+  @Output() updateModel = new EventEmitter<{ property: string; value: string | boolean }>();
+}
-- 
GitLab