From 7626dd2e8504b6173858db79254f266a49de87cd Mon Sep 17 00:00:00 2001
From: rhenck <richard.henck@iqb.hu-berlin.de>
Date: Thu, 10 Mar 2022 17:16:10 +0100
Subject: [PATCH] [editor] Refactor dynamic grid view

Now uses the implicit grid in auto mode.
---
 projects/editor/src/app/app.module.ts         |   6 +-
 .../page-view/canvas/canvas.component.html    |   6 +-
 .../dynamic-section-helper-grid.component.ts  | 153 ++++++++++++++++
 .../element-grid-change-listener.directive.ts |  20 ++
 .../canvas/section-dynamic.component.ts       | 125 ++++---------
 .../canvas/section-menu.component.ts          | 171 ++++++++++--------
 projects/editor/src/assets/i18n/de.json       |   3 +-
 7 files changed, 308 insertions(+), 176 deletions(-)
 create mode 100644 projects/editor/src/app/components/unit-view/page-view/canvas/dynamic-section-helper-grid.component.ts
 create mode 100644 projects/editor/src/app/components/unit-view/page-view/canvas/element-grid-change-listener.directive.ts

diff --git a/projects/editor/src/app/app.module.ts b/projects/editor/src/app/app.module.ts
index 71b9bec7b..5bb043572 100644
--- a/projects/editor/src/app/app.module.ts
+++ b/projects/editor/src/app/app.module.ts
@@ -59,6 +59,8 @@ import { ElementStylePropertiesComponent } from
   './components/unit-view/page-view/properties-panel/style-properties-tab/element-style-properties.component';
 import { ElementModelPropertiesComponent } from
   './components/unit-view/page-view/properties-panel/model-properties-tab/element-model-properties.component';
+import { DynamicSectionHelperGridComponent } from './components/unit-view/page-view/canvas/dynamic-section-helper-grid.component';
+import { ElementGridChangeListenerDirective } from './components/unit-view/page-view/canvas/element-grid-change-listener.directive';
 
 @NgModule({
   declarations: [
@@ -89,7 +91,9 @@ import { ElementModelPropertiesComponent } from
     ElementModelPropertiesComponent,
     DropListOptionEditDialogComponent,
     PositionFieldSetComponent,
-    DimensionFieldSetComponent
+    DimensionFieldSetComponent,
+    DynamicSectionHelperGridComponent,
+    ElementGridChangeListenerDirective
   ],
   imports: [
     BrowserModule,
diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/canvas.component.html b/projects/editor/src/app/components/unit-view/page-view/canvas/canvas.component.html
index 73d7963ef..3f7e70675 100644
--- a/projects/editor/src/app/components/unit-view/page-view/canvas/canvas.component.html
+++ b/projects/editor/src/app/components/unit-view/page-view/canvas/canvas.component.html
@@ -1,7 +1,7 @@
 <div class="canvasBackground" fxFlex
      (click)="selectionService.clearElementSelection()">
   <div class="canvasFrame"
-       [style.width.px]="page.hasMaxWidth ? page.maxWidth : 900"
+       [style.width.px]="page.hasMaxWidth ? page.maxWidth : null"
        [style.padding.px]="page.margin"
        [style.background-color]="page.backgroundColor">
     <div cdkDropListGroup>
@@ -48,9 +48,7 @@
   </div>
 
   <button mat-fab class="add-section-button"
-          [style.width.px]="page.hasMaxWidth ? page.maxWidth : 900"
-          [style.margin-left.px]="page.margin"
-          [style.margin-right.px]="page.margin"
+          [style.width.px]="page.hasMaxWidth ? page.maxWidth : null"
           (click)="addSection()">
     <mat-icon>add</mat-icon>
   </button>
diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/dynamic-section-helper-grid.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/dynamic-section-helper-grid.component.ts
new file mode 100644
index 000000000..f44024a46
--- /dev/null
+++ b/projects/editor/src/app/components/unit-view/page-view/canvas/dynamic-section-helper-grid.component.ts
@@ -0,0 +1,153 @@
+import { CdkDragDrop } from '@angular/cdk/drag-drop/drag-events';
+import {
+  ChangeDetectorRef,
+  Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges
+} from '@angular/core';
+import { UIElement, UIElementType } from '../../../../../../../common/interfaces/elements';
+import { UnitService } from '../../../../services/unit.service';
+import { Section } from '../../../../../../../common/interfaces/unit';
+
+@Component({
+  selector: '[app-dynamic-section-helper-grid]',
+  template: `
+    <ng-container *ngFor="let column of columnCountArray; let x = index">
+      <ng-container *ngFor="let row of rowCountArray; let y = index">
+        <div class="grid-placeholder"
+             [style.grid-column-start]="x + 1"
+             [style.grid-column-end]="x + 1"
+             [style.grid-row-start]="y + 1"
+             [style.grid-row-end]="y + 1"
+             cdkDropList [cdkDropListData]="{ sectionIndex: sectionIndex, gridCoordinates: [x + 1, y + 1] }"
+             (cdkDropListDropped)="drop($any($event))"
+             id="list-{{sectionIndex}}-{{x+1}}-{{y+1}}"
+             (dragover)="$event.preventDefault()"
+             (drop)="newElementDropped( $event, x + 1, y + 1)">
+          {{x + 1}} / {{y + 1}}
+        </div>
+      </ng-container>
+    </ng-container>
+
+    <ng-content></ng-content>
+  `,
+  styles: [
+    '.grid-placeholder {border: 5px solid aliceblue; color: lightblue; text-align: center;}'
+  ]
+})
+export class DynamicSectionHelperGridComponent implements OnInit, OnChanges {
+  @Input() autoColumnSize!: boolean;
+  @Input() autoRowSize!: boolean;
+  @Input() gridColumnSizes!: string;
+  @Input() gridRowSizes!: string;
+  @Input() section!: Section;
+  @Input() sectionIndex!: number;
+  @Output() transferElement = new EventEmitter<{ previousSectionIndex: number, newSectionIndex: number }>();
+
+  columnCountArray: unknown[] = [];
+  rowCountArray: unknown[] = [];
+
+  constructor(public unitService: UnitService) {}
+
+  ngOnInit(): void {
+    this.calculateColumnCount();
+    this.calculateRowCount();
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes.autoColumnSize ||
+      changes.gridColumnSizes ||
+      changes.gridRowSizes) {
+      this.calculateColumnCount();
+      this.calculateRowCount();
+    }
+  }
+
+  refresh(): void {
+    this.calculateColumnCount();
+    this.calculateRowCount();
+  }
+
+  private calculateColumnCount(): void {
+    let numberOfColumns;
+    if (this.autoColumnSize) {
+      numberOfColumns = this.section.elements
+        .reduce((accumulator, currentValue) => Math.max(accumulator, currentValue.position.gridColumnEnd as number),
+          0) - 1;
+    } else {
+      numberOfColumns = this.gridColumnSizes.split(' ').length;
+    }
+    this.columnCountArray = Array(Math.max(numberOfColumns, 1));
+  }
+
+  private calculateRowCount(): void {
+    let numberOfRows;
+    if (this.autoRowSize) {
+      numberOfRows = this.section.elements
+        .reduce((accumulator, currentValue) => Math.max(accumulator, currentValue.position.gridRowEnd as number),
+          0) - 1;
+    } else {
+      numberOfRows = this.gridRowSizes.split(' ').length;
+    }
+    this.rowCountArray = Array(Math.max(numberOfRows, 1));
+  }
+
+  drop(event: CdkDragDrop<{ sectionIndex: number; gridCoordinates?: number[]; }>): void {
+    const dragItemData: { dragType: string; element: UIElement; } = event.item.data;
+
+    // Move element to other section - handled by parent (page-canvas).
+    if (event.previousContainer.data.sectionIndex !== event.container.data.sectionIndex) {
+      this.transferElement.emit({
+        previousSectionIndex: event.previousContainer.data.sectionIndex,
+        newSectionIndex: event.container.data.sectionIndex
+      });
+    }
+    if (dragItemData.dragType === 'move') {
+      const elementColumnRange: number =
+        event.item.data.element.position.gridColumnEnd - event.item.data.element.position.gridColumnStart;
+      const elementRowRange: number =
+        event.item.data.element.position.gridRowEnd - event.item.data.element.position.gridRowStart;
+      this.unitService.updateElementProperty(
+        [event.item.data.element],
+        'gridColumnStart',
+        event.container.data.gridCoordinates![0]
+      );
+      // Ensure the end value is at least the same as the start, otherwise the grid breaks.
+      this.unitService.updateElementProperty(
+        [dragItemData.element],
+        'gridColumnEnd',
+        event.container.data.gridCoordinates![0] + elementColumnRange
+      );
+      this.unitService.updateElementProperty(
+        [dragItemData.element],
+        'gridRowStart',
+        event.container.data.gridCoordinates![1]
+      );
+      this.unitService.updateElementProperty(
+        [dragItemData.element],
+        'gridRowEnd',
+        event.container.data.gridCoordinates![1] + elementRowRange
+      );
+    } else if (event.item.data.dragType === 'resize') {
+      this.unitService.updateElementProperty(
+        [dragItemData.element],
+        'gridColumnEnd',
+        event.container.data.gridCoordinates![0] + 1
+      );
+      this.unitService.updateElementProperty(
+        [dragItemData.element],
+        'gridRowEnd',
+        event.container.data.gridCoordinates![1] + 1
+      );
+    } else {
+      throw new Error('Unknown drop event');
+    }
+  }
+
+  newElementDropped(event: DragEvent, gridX: number, gridY: number): void {
+    event.preventDefault();
+    this.unitService.addElementToSection(
+      event.dataTransfer?.getData('elementType') as UIElementType,
+      this.section,
+      { x: gridX, y: gridY }
+    );
+  }
+}
diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/element-grid-change-listener.directive.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/element-grid-change-listener.directive.ts
new file mode 100644
index 000000000..5378abece
--- /dev/null
+++ b/projects/editor/src/app/components/unit-view/page-view/canvas/element-grid-change-listener.directive.ts
@@ -0,0 +1,20 @@
+import {
+  Directive, EventEmitter, Input, OnChanges, Output
+} from '@angular/core';
+
+@Directive({
+  selector: '[appElementGridChangeListener]'
+})
+export class ElementGridChangeListenerDirective implements OnChanges {
+  @Input() autoColumnSize!: boolean;
+  @Input() gridColumnStart!: number;
+  @Input() gridColumnEnd!: number;
+  @Input() gridRowStart!: number;
+  @Input() gridRowEnd!: number;
+
+  @Output() elementChanged = new EventEmitter();
+
+  ngOnChanges(): void {
+    this.elementChanged.emit();
+  }
+}
diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/section-dynamic.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/section-dynamic.component.ts
index 963768cbb..11e320851 100644
--- a/projects/editor/src/app/components/unit-view/page-view/canvas/section-dynamic.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/canvas/section-dynamic.component.ts
@@ -1,11 +1,14 @@
 import {
-  Component, Input, Output, EventEmitter, ViewChildren, QueryList
+  Component,
+  Input,
+  Output,
+  EventEmitter,
+  ViewChildren,
+  QueryList, ViewChild, ElementRef
 } from '@angular/core';
-import { CdkDragDrop } from '@angular/cdk/drag-drop/drag-events';
-import { UnitService } from '../../../../services/unit.service';
 import { CanvasElementOverlay } from './overlays/canvas-element-overlay';
 import { Section } from '../../../../../../../common/interfaces/unit';
-import { UIElement, UIElementType } from '../../../../../../../common/interfaces/elements';
+import { DynamicSectionHelperGridComponent } from './dynamic-section-helper-grid.component';
 
 @Component({
   selector: 'aspect-section-dynamic',
@@ -13,30 +16,23 @@ import { UIElement, UIElementType } from '../../../../../../../common/interfaces
     <div [style.display]="'grid'"
          [style.grid-template-columns]="section.autoColumnSize ? '' : section.gridColumnSizes"
          [style.grid-template-rows]="section.autoRowSize ? '' : section.gridRowSizes"
-         [style.height.%]="100"
+         [style.grid-auto-columns]="'auto'"
+         [style.grid-auto-rows]="'auto'"
          cdkDropListGroup
          [style.border]="isSelected ? '2px solid #ff4081': '1px dotted'"
-         [style.height.px]="section.height"
-         [style.background-color]="section.backgroundColor">
-
-      <!-- Dynamic sections have the droplists for the grid cells next to the actual elements. Elements can not
-           be children of the grid cells because they can span over multiple cells. -->
-      <ng-container *ngFor="let column of this.section.gridColumnSizes.split(' '); let x = index">
-        <ng-container *ngFor="let row of this.section.gridRowSizes.split(' '); let y = index">
-          <div class="grid-placeholder"
-               [style.grid-column-start]="x + 1"
-               [style.grid-column-end]="x + 1"
-               [style.grid-row-start]="y + 1"
-               [style.grid-row-end]="y + 1"
-               cdkDropList [cdkDropListData]="{ sectionIndex: sectionIndex, gridCoordinates: [x + 1, y + 1] }"
-               (cdkDropListDropped)="drop($any($event))"
-               id="list-{{sectionIndex}}-{{x+1}}-{{y+1}}"
-               (dragover)="$event.preventDefault()" (drop)="newElementDropped($event, x + 1, y + 1)">
-              {{x + 1}} / {{y + 1}}
-          </div>
-        </ng-container>
-      </ng-container>
+         [style.min-height.px]="section.autoRowSize ? 50 : section.height"
+         [style.background-color]="section.backgroundColor"
+         app-dynamic-section-helper-grid
+         [autoColumnSize]="section.autoColumnSize"
+         [autoRowSize]="section.autoRowSize"
+         [gridColumnSizes]="section.gridColumnSizes"
+         [gridRowSizes]="section.gridRowSizes"
+         [section]="section"
+         [sectionIndex]="sectionIndex"
+         (transferElement)="transferElement.emit($event)">
 
+      <!-- Angular content projection is used in the helper grid component, where the following
+           is the content.-->
       <aspect-dynamic-canvas-overlay *ngFor="let element of section.elements"
                                      #elementComponent
                                      [element]="$any(element)"
@@ -53,15 +49,18 @@ import { UIElement, UIElementType } from '../../../../../../../common/interfaces
                                      cdkDropList cdkDropListSortingDisabled
                                      [cdkDropListData]="{ sectionIndex: sectionIndex }"
                                      [cdkDropListConnectedTo]="dropListList"
-                                     (resize)="resizeOverlay($event)"
                                      [style.position]="'relative'"
-                                     [style.pointer-events]="dragging ? 'none' : 'auto'">
+                                     [style.pointer-events]="dragging ? 'none' : 'auto'"
+                                     appElementGridChangeListener
+                                     [gridColumnStart]="element.position.gridColumnStart"
+                                     [gridColumnEnd]="element.position.gridColumnEnd"
+                                     [gridRowStart]="element.position.gridRowStart"
+                                     [gridRowEnd]="element.position.gridRowEnd"
+                                     (resize)="resizeOverlay($event)"
+                                     (elementChanged)="helperGrid?.refresh()">
       </aspect-dynamic-canvas-overlay>
     </div>
-  `,
-  styles: [
-    '.grid-placeholder {border: 5px solid aliceblue; color: lightblue; text-align: center;}'
-  ]
+  `
 })
 export class SectionDynamicComponent {
   @Input() section!: Section;
@@ -70,73 +69,11 @@ export class SectionDynamicComponent {
   @Input() isSelected!: boolean;
   @Output() transferElement = new EventEmitter<{ previousSectionIndex: number, newSectionIndex: number }>();
 
+  @ViewChild(DynamicSectionHelperGridComponent) helperGrid!: DynamicSectionHelperGridComponent;
   @ViewChildren('elementComponent') childElementComponents!: QueryList<CanvasElementOverlay>;
 
   dragging = false;
 
-  constructor(public unitService: UnitService) { }
-
-  drop(event: CdkDragDrop<{ sectionIndex: number; gridCoordinates?: number[]; }>): void {
-    const dragItemData: { dragType: string; element: UIElement; } = event.item.data;
-
-    // Move element to other section - handled by parent (page-canvas).
-    if (event.previousContainer.data.sectionIndex !== event.container.data.sectionIndex) {
-      this.transferElement.emit({
-        previousSectionIndex: event.previousContainer.data.sectionIndex,
-        newSectionIndex: event.container.data.sectionIndex
-      });
-    }
-    if (dragItemData.dragType === 'move') {
-      const elementColumnRange: number =
-        event.item.data.element.position.gridColumnEnd - event.item.data.element.position.gridColumnStart;
-      const elementRowRange: number =
-        event.item.data.element.position.gridRowEnd - event.item.data.element.position.gridRowStart;
-      this.unitService.updateElementProperty(
-        [event.item.data.element],
-        'gridColumnStart',
-        event.container.data.gridCoordinates![0]
-      );
-      // Ensure the end value is at least the same as the start, otherwise the grid breaks.
-      this.unitService.updateElementProperty(
-        [dragItemData.element],
-        'gridColumnEnd',
-        event.container.data.gridCoordinates![0] + elementColumnRange
-      );
-      this.unitService.updateElementProperty(
-        [dragItemData.element],
-        'gridRowStart',
-        event.container.data.gridCoordinates![1]
-      );
-      this.unitService.updateElementProperty(
-        [dragItemData.element],
-        'gridRowEnd',
-        event.container.data.gridCoordinates![1] + elementRowRange
-      );
-    } else if (event.item.data.dragType === 'resize') {
-      this.unitService.updateElementProperty(
-        [dragItemData.element],
-        'gridColumnEnd',
-        event.container.data.gridCoordinates![0] + 1
-      );
-      this.unitService.updateElementProperty(
-        [dragItemData.element],
-        'gridRowEnd',
-        event.container.data.gridCoordinates![1] + 1
-      );
-    } else {
-      throw new Error('Unknown drop event');
-    }
-  }
-
-  newElementDropped(event: DragEvent, gridX: number, gridY: number): void {
-    event.preventDefault();
-    this.unitService.addElementToSection(
-      event.dataTransfer?.getData('elementType') as UIElementType,
-      this.section,
-      { x: gridX, y: gridY }
-    );
-  }
-
   resizeOverlay(event: { dragging: boolean, elementWidth?: number, elementHeight?: number }): void {
     this.dragging = event.dragging;
   }
diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/section-menu.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/section-menu.component.ts
index 8611d95f8..54324c69f 100644
--- a/projects/editor/src/app/components/unit-view/page-view/canvas/section-menu.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/canvas/section-menu.component.ts
@@ -18,26 +18,16 @@ import { UIElement } from '../../../../../../../common/interfaces/elements';
     </button>
     <mat-menu #elementListMenu="matMenu" class="layoutMenu" xPosition="before">
       <mat-action-list>
+        <ng-container *ngIf="section.elements.length === 0">
+            Keine Elemente im Abschnitt
+        </ng-container>
         <mat-list-item *ngFor="let element of section.elements"
-                         (click)="selectElement(element)">
+                       (click)="selectElement(element)">
           {{element.id}}
         </mat-list-item>
       </mat-action-list>
     </mat-menu>
-    <button mat-mini-fab [matMenuTriggerFor]="heightMenu">
-      <mat-icon>height</mat-icon>
-    </button>
-    <mat-menu #heightMenu="matMenu" class="layoutMenu" xPosition="before">
-      <div (click)="$event.stopPropagation()">
-        <mat-form-field appearance="fill">
-          <mat-label>{{'section-menu.height' | translate }}</mat-label>
-          <input matInput mat-menu-item type="number"
-                 [value]="$any(section.height)"
-                 (click)="$any($event).stopPropagation()"
-                 (change)="updateModel('height', $any($event.target).value)">
-        </mat-form-field>
-      </div>
-    </mat-menu>
+
     <button mat-mini-fab
             (click)="openColorPicker()">
       <mat-icon>palette</mat-icon>
@@ -51,76 +41,105 @@ import { UIElement } from '../../../../../../../common/interfaces/elements';
     </button>
     <mat-menu #layoutMenu="matMenu" class="layoutMenu" xPosition="before">
       <div (click)="$event.stopPropagation()">
-          <mat-checkbox class="menuItem" [checked]="section.dynamicPositioning"
-                        (click)="$any($event).stopPropagation()"
-                        (change)="updateModel('dynamicPositioning', $event.checked)">
-            {{'section-menu.dynamic-positioning' | translate }}
-          </mat-checkbox>
+        <mat-checkbox class="menuItem" [checked]="section.dynamicPositioning"
+                      (click)="$any($event).stopPropagation()"
+                      (change)="updateModel('dynamicPositioning', $event.checked)">
+          {{'section-menu.dynamic-positioning' | translate }}
+        </mat-checkbox>
+        <ng-container *ngIf="!section.dynamicPositioning">
+          <mat-form-field appearance="fill">
+            <mat-label>{{'section-menu.height' | translate }}</mat-label>
+            <input matInput mat-menu-item type="number"
+                   [value]="$any(section.height)"
+                   (click)="$any($event).stopPropagation()"
+                   (change)="updateModel('height', $any($event.target).value)">
+          </mat-form-field>
+        </ng-container>
         <div *ngIf="section.dynamicPositioning">
-          {{'section-menu.columns' | translate }}
-          <div class="size-group">
-            <mat-form-field>
-              <mat-label>{{'section-menu.amount' | translate }}</mat-label>
-              <input matInput type="number"
-                     [value]="$any(section.gridColumnSizes.split(' ').length)"
-                     (click)="$any($event).stopPropagation()"
-                     (change)="modifySizeArray('gridColumnSizes', $any($event).target.value)">
-            </mat-form-field>
-            <mat-checkbox class="menuItem" [checked]="section.autoColumnSize"
-                          (click)="$any($event).stopPropagation()"
-                          (change)="updateModel('autoColumnSize', $event.checked)">
-              {{'section-menu.autoColumnSize' | translate }}
-            </mat-checkbox>
-            <ng-container *ngIf="!section.autoColumnSize">
-              <div *ngFor="let size of columnSizes ; let i = index" class="size-inputs" fxLayout="row">
+          <fieldset>
+            <legend>{{'section-menu.columns' | translate }}</legend>
+
+            <div class="size-group">
+              <mat-checkbox class="menuItem" [checked]="section.autoColumnSize"
+                            (click)="$any($event).stopPropagation()"
+                            (change)="updateModel('autoColumnSize', $event.checked)">
+                {{'section-menu.autoColumnSize' | translate }}
+              </mat-checkbox>
+              <ng-container *ngIf="!section.autoColumnSize">
                 <mat-form-field>
-                  <mat-label>{{'section-menu.width' | translate }} {{i + 1}}</mat-label>
+                  <mat-label>{{'section-menu.columnCount' | translate }}</mat-label>
                   <input matInput type="number"
-                         [value]="size.value"
+                         [value]="$any(section.gridColumnSizes.split(' ').length)"
                          (click)="$any($event).stopPropagation()"
-                         (change)="changeGridSize('gridColumnSizes', i, false, $any($event).target.value)">
+                         (change)="modifySizeArray('gridColumnSizes', $any($event).target.value)">
                 </mat-form-field>
-                <mat-select [value]="size.unit"
+                <div *ngFor="let size of columnSizes ; let i = index" class="size-inputs" fxLayout="row">
+                  <mat-form-field>
+                    <mat-label>{{'section-menu.width' | translate }} {{i + 1}}</mat-label>
+                    <input matInput type="number"
+                           [value]="size.value"
+                           (click)="$any($event).stopPropagation()"
+                           (change)="changeGridSize('gridColumnSizes', i, false, $any($event).target.value)">
+                  </mat-form-field>
+                  <mat-select [value]="size.unit"
+                              (click)="$any($event).stopPropagation()"
+                              (selectionChange)="changeGridSize('gridColumnSizes', i, true, $event.value)">
+                    <mat-option value="fr">{{'section-menu.fraction' | translate }}</mat-option>
+                    <mat-option value="px">{{'section-menu.pixel' | translate }}</mat-option>
+                  </mat-select>
+                </div>
+              </ng-container>
+            </div>
+          </fieldset>
+
+          <fieldset>
+            <legend>{{'section-menu.rows' | translate }}</legend>
+            <div class="size-group">
+              <mat-checkbox class="menuItem" [checked]="section.autoRowSize"
                             (click)="$any($event).stopPropagation()"
-                            (selectionChange)="changeGridSize('gridColumnSizes', i, true, $event.value)">
-                  <mat-option value="fr">{{'section-menu.fraction' | translate }}</mat-option>
-                  <mat-option value="px">{{'section-menu.pixel' | translate }}</mat-option>
-                </mat-select>
-              </div>
-            </ng-container>
-          </div>
-          {{'section-menu.rows' | translate }}
-          <div class="size-group">
-            <mat-form-field>
-              <mat-label>{{'section-menu.amount' | translate }}</mat-label>
-              <input matInput type="number"
-                     [value]="$any(section.gridRowSizes.split(' ').length)"
-                     (click)="$any($event).stopPropagation()"
-                     (change)="modifySizeArray('gridRowSizes', $any($event).target.value)">
-            </mat-form-field>
-            <mat-checkbox class="menuItem" [checked]="section.autoRowSize"
-                          (click)="$any($event).stopPropagation()"
-                          (change)="updateModel('autoRowSize', $event.checked)">
-              {{'section-menu.autoRowSize' | translate }}
-            </mat-checkbox>
-            <ng-container *ngIf="!section.autoRowSize">
-              <div *ngFor="let size of rowSizes ; let i = index" class="size-inputs" fxLayout="row">
+                            (change)="updateModel('autoRowSize', $event.checked)">
+                {{'section-menu.autoRowSize' | translate }}
+              </mat-checkbox>
+              <ng-container *ngIf="!section.autoRowSize">
+
+                <mat-form-field appearance="fill">
+                  <mat-label>{{'section-menu.height' | translate }}</mat-label>
+                  <input matInput mat-menu-item type="number"
+                         [value]="$any(section.height)"
+                         (click)="$any($event).stopPropagation()"
+                         (change)="updateModel('height', $any($event.target).value)">
+                </mat-form-field>
+
                 <mat-form-field>
-                  <mat-label>{{'section-menu.height' | translate }} {{i + 1}}</mat-label>
+                  <mat-label>{{'section-menu.rowCount' | translate }}</mat-label>
                   <input matInput type="number"
-                         [value]="size.value"
+                         [value]="$any(section.gridRowSizes.split(' ').length)"
                          (click)="$any($event).stopPropagation()"
-                         (change)="changeGridSize('gridRowSizes', i, false, $any($event).target.value)">
+                         (change)="modifySizeArray('gridRowSizes', $any($event).target.value)">
                 </mat-form-field>
-                <mat-select [value]="size.unit"
-                            (click)="$any($event).stopPropagation()"
-                            (selectionChange)="changeGridSize('gridRowSizes', i, true, $event.value)">
-                  <mat-option value="fr">{{'section-menu.fraction' | translate }}</mat-option>
-                  <mat-option value="px">{{'section-menu.pixel' | translate }}</mat-option>
-                </mat-select>
-              </div>
-            </ng-container>
-          </div>
+
+
+                <fieldset>
+                  <legend>Zeilenhöhen</legend>
+                  <div *ngFor="let size of rowSizes ; let i = index" class="size-inputs" fxLayout="row">
+                    <mat-form-field>
+                      <mat-label>{{'section-menu.height' | translate }} {{i + 1}}</mat-label>
+                      <input matInput type="number"
+                             [value]="size.value"
+                             (click)="$any($event).stopPropagation()"
+                             (change)="changeGridSize('gridRowSizes', i, false, $any($event).target.value)">
+                    </mat-form-field>
+                    <mat-select [value]="size.unit"
+                                (click)="$any($event).stopPropagation()"
+                                (selectionChange)="changeGridSize('gridRowSizes', i, true, $event.value)">
+                      <mat-option value="fr">{{'section-menu.fraction' | translate }}</mat-option>
+                      <mat-option value="px">{{'section-menu.pixel' | translate }}</mat-option>
+                    </mat-select>
+                  </div>
+                </fieldset>
+              </ng-container>
+            </div>
+          </fieldset>
         </div>
       </div>
     </mat-menu>
@@ -146,7 +165,7 @@ import { UIElement } from '../../../../../../../common/interfaces/elements';
     '::ng-deep .layoutMenu .size-inputs .mat-form-field-infix {width: 65px}',
     '.size-group {background-color: #f5f5f5; margin: 0 0 15px 0}',
     '::ng-deep .layoutMenu .size-group mat-select {padding-top: 24px; padding-left: 15px;}',
-    '::ng-deep .layoutMenu {width: 200px; padding: 0 15px}'
+    '::ng-deep .layoutMenu {padding: 0 15px}'
   ]
 })
 export class SectionMenuComponent implements OnInit, OnDestroy {
diff --git a/projects/editor/src/assets/i18n/de.json b/projects/editor/src/assets/i18n/de.json
index a7bb8bf76..0bf327a45 100644
--- a/projects/editor/src/assets/i18n/de.json
+++ b/projects/editor/src/assets/i18n/de.json
@@ -195,7 +195,8 @@
     "dynamic-positioning": "dynamisches Layout",
     "columns": "Spalten",
     "rows": "Zeilen",
-    "amount": "Anzahl",
+    "columnCount": "Anzahl der Spalten",
+    "rowCount": "Anzahl der Zeilen",
     "autoColumnSize": "dynamische Breite",
     "autoRowSize": "dynamische Höhe",
     "width": "Breite",
-- 
GitLab