From 024709a61760e033158c4d461dcc7e385f27bd1c Mon Sep 17 00:00:00 2001
From: rhenck <richard.henck@iqb.hu-berlin.de>
Date: Thu, 2 Dec 2021 18:53:17 +0100
Subject: [PATCH] Add Frame element

Basically a div with border properties as usual.
---
 projects/common/id.service.ts                 |  1 +
 projects/common/models/uI-element.ts          |  2 +-
 projects/common/shared.module.ts              |  4 +-
 .../common/ui-elements/frame/frame-element.ts | 29 ++++++++++++++
 .../ui-elements/frame/frame.component.ts      | 23 +++++++++++
 projects/common/util/element.factory.ts       |  7 ++++
 .../ui-element-toolbox.component.html         |  5 +++
 .../element-model-properties.component.html   | 38 +++++++++++++++++++
 8 files changed, 107 insertions(+), 2 deletions(-)
 create mode 100644 projects/common/ui-elements/frame/frame-element.ts
 create mode 100644 projects/common/ui-elements/frame/frame.component.ts

diff --git a/projects/common/id.service.ts b/projects/common/id.service.ts
index 8acc04e0e..e45dca5a6 100644
--- a/projects/common/id.service.ts
+++ b/projects/common/id.service.ts
@@ -21,6 +21,7 @@ export class IdService {
     'radio-group-images': 0,
     'drop-list': 0,
     cloze: 0,
+    frame: 0,
     value: 0
   };
 
diff --git a/projects/common/models/uI-element.ts b/projects/common/models/uI-element.ts
index 0ddd0d2a4..66ba3718a 100644
--- a/projects/common/models/uI-element.ts
+++ b/projects/common/models/uI-element.ts
@@ -3,7 +3,7 @@ import { IdService } from '../id.service';
 
 export type UIElementType = 'text' | 'button' | 'text-field' | 'text-area' | 'checkbox'
 | 'dropdown' | 'radio' | 'image' | 'audio' | 'video' | 'likert' | 'likert_row' | 'radio-group-images'
-| 'drop-list' | 'cloze' | 'spell-correct' | 'slider';
+| 'drop-list' | 'cloze' | 'spell-correct' | 'slider' | 'frame';
 export type InputElementValue = string[] | string | number | boolean | DragNDropValueObject[] | null;
 export type DragNDropValueObject = {
   id: string;
diff --git a/projects/common/shared.module.ts b/projects/common/shared.module.ts
index cbd0a0661..199ea52de 100644
--- a/projects/common/shared.module.ts
+++ b/projects/common/shared.module.ts
@@ -46,6 +46,7 @@ import { TextFieldSimpleComponent } from './ui-elements/textfield-simple/text-fi
 import { SliderComponent } from './ui-elements/slider/slider.component';
 import { SpellCorrectComponent } from './ui-elements/spell-correct/spell-correct.component';
 import { DropListSimpleComponent } from './ui-elements/drop-list-simple/drop-list-simple.component';
+import { FrameComponent } from './ui-elements/frame/frame.component';
 
 @NgModule({
   imports: [
@@ -93,7 +94,8 @@ import { DropListSimpleComponent } from './ui-elements/drop-list-simple/drop-lis
     DropListSimpleComponent,
     SliderComponent,
     SpellCorrectComponent,
-    TextFieldSimpleComponent
+    TextFieldSimpleComponent,
+    FrameComponent
   ],
   exports: [
     CommonModule,
diff --git a/projects/common/ui-elements/frame/frame-element.ts b/projects/common/ui-elements/frame/frame-element.ts
new file mode 100644
index 000000000..8c1efb115
--- /dev/null
+++ b/projects/common/ui-elements/frame/frame-element.ts
@@ -0,0 +1,29 @@
+import {
+  UIElement,
+  PositionedElement,
+  SurfaceElement,
+  SurfaceProperties,
+  PositionProperties
+} from '../../models/uI-element';
+import { initPositionedElement, initSurfaceElement } from '../../util/unit-interface-initializer';
+
+export class FrameElement extends UIElement implements PositionedElement, SurfaceElement {
+  borderWidth: number = 1;
+  borderColor: string = 'black';
+  borderStyle: 'hidden' | 'solid' | 'dotted' | 'dashed' | 'double' | 'groove' | 'ridge' | 'inset' | 'outset' = 'solid';
+  borderRadius: number = 0;
+
+  positionProps: PositionProperties;
+  surfaceProps: SurfaceProperties;
+
+  constructor(serializedElement: UIElement) {
+    super(serializedElement);
+    Object.assign(this, serializedElement);
+    this.positionProps = initPositionedElement(serializedElement);
+    this.surfaceProps = initSurfaceElement(serializedElement);
+
+    this.surfaceProps.backgroundColor =
+      serializedElement.surfaceProps?.backgroundColor as string ||
+      'transparent';
+  }
+}
diff --git a/projects/common/ui-elements/frame/frame.component.ts b/projects/common/ui-elements/frame/frame.component.ts
new file mode 100644
index 000000000..dde15dea6
--- /dev/null
+++ b/projects/common/ui-elements/frame/frame.component.ts
@@ -0,0 +1,23 @@
+import { Component, Input } from '@angular/core';
+import { FrameElement } from './frame-element';
+
+@Component({
+  selector: 'app-frame',
+  template: `
+    <div [style.width]="elementModel.borderStyle !== 'hidden' ?
+                            'calc(100% - ' + (elementModel.borderWidth * 2) + 'px)' :
+                            '100%'"
+         [style.height]="elementModel.borderStyle !== 'hidden' ?
+                            'calc(100% - ' + (elementModel.borderWidth * 2) + 'px)' :
+                            '100%'"
+         [style.border-style]="elementModel.borderStyle"
+         [style.border-width.px]="elementModel.borderStyle !== 'hidden' ? elementModel.borderWidth : ''"
+         [style.border-color]="elementModel.borderColor"
+         [style.border-radius.px]="elementModel.borderRadius"
+         [style.background-color]="elementModel.surfaceProps.backgroundColor">
+  </div>
+  `
+})
+export class FrameComponent {
+  @Input() elementModel!: FrameElement;
+}
diff --git a/projects/common/util/element.factory.ts b/projects/common/util/element.factory.ts
index deddd7dcd..4ad43d1b0 100644
--- a/projects/common/util/element.factory.ts
+++ b/projects/common/util/element.factory.ts
@@ -32,6 +32,8 @@ import { SliderElement } from '../ui-elements/slider/slider-element';
 import { SpellCorrectElement } from '../ui-elements/spell-correct/spell-correct-element';
 import { SliderComponent } from '../ui-elements/slider/slider.component';
 import { SpellCorrectComponent } from '../ui-elements/spell-correct/spell-correct.component';
+import { FrameComponent } from '../ui-elements/frame/frame.component';
+import { FrameElement } from '../ui-elements/frame/frame-element';
 
 export function createElement(elementModel: UIElement): UIElement {
   let newElement: UIElement;
@@ -84,6 +86,9 @@ export function createElement(elementModel: UIElement): UIElement {
     case 'spell-correct':
       newElement = new SpellCorrectElement(elementModel);
       break;
+    case 'frame':
+      newElement = new FrameElement(elementModel);
+      break;
     default:
       throw new Error(`ElementType ${elementModel.type} not found!`);
   }
@@ -128,6 +133,8 @@ export function getComponentFactory(
       return componentFactoryResolver.resolveComponentFactory(SliderComponent);
     case 'spell-correct':
       return componentFactoryResolver.resolveComponentFactory(SpellCorrectComponent);
+    case 'frame':
+      return componentFactoryResolver.resolveComponentFactory(FrameComponent);
     default:
       throw new Error('unknown element');
   }
diff --git a/projects/editor/src/app/components/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html b/projects/editor/src/app/components/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html
index 5b5deeb3f..2ff12016b 100644
--- a/projects/editor/src/app/components/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html
+++ b/projects/editor/src/app/components/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html
@@ -14,6 +14,11 @@
         <mat-icon>smart_button</mat-icon>
         Knopf
       </button>
+      <button mat-raised-button (click)="addUIElement('frame')"
+              draggable="true" (dragstart)="$event.dataTransfer?.setData('elementType','frame')">
+        <mat-icon>crop_square</mat-icon>
+        Rahmen
+      </button>
       <button mat-raised-button (click)="addUIElement('text-field')"
               draggable="true" (dragstart)="$event.dataTransfer?.setData('elementType','text-field')">
         <mat-icon>text_fields</mat-icon>
diff --git a/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-model-properties.component.html b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-model-properties.component.html
index d3a5fc8a4..4c23420e5 100644
--- a/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-model-properties.component.html
+++ b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-model-properties.component.html
@@ -539,4 +539,42 @@
     <input matInput type="number" [value]="combinedProperties.firstColumnSizeRatio"
            (input)="updateModel.emit({ property: 'firstColumnSizeRatio', value: $any($event.target).value })">
   </mat-form-field>
+
+
+
+  <mat-form-field *ngIf="combinedProperties.borderStyle !== undefined"
+                  appearance="fill">
+    <mat-label>{{'propertiesPanel.borderStyle' | translate }}</mat-label>
+    <mat-select [value]="combinedProperties.borderStyle"
+                (selectionChange)="updateModel.emit({ property: 'borderStyle', value: $event.value })">
+      <mat-option *ngFor="let option of ['hidden', 'solid', 'dotted', 'dashed',
+                                         'double', 'groove', 'ridge', 'inset', 'outset']"
+                  [value]="option">
+        {{ 'propertiesPanel.' + option | translate }}
+      </mat-option>
+    </mat-select>
+  </mat-form-field>
+
+  <mat-form-field *ngIf="combinedProperties.borderColor !== undefined"
+                  appearance="fill" class="mdInput textsingleline">
+    <mat-label>{{'propertiesPanel.borderColor' | translate }}</mat-label>
+    <input matInput type="color" [value]="combinedProperties.borderColor"
+           (input)="updateModel.emit({ property: 'borderColor', value: $any($event.target).value })">
+  </mat-form-field>
+  <mat-form-field *ngIf="combinedProperties.borderColor !== undefined"
+                  appearance="fill" class="mdInput textsingleline">
+    <mat-label>{{'propertiesPanel.borderColor' | translate }}</mat-label>
+    <input matInput type="text" [value]="combinedProperties.borderColor"
+           (input)="updateModel.emit({ property: 'borderColor', value: $any($event.target).value })">
+  </mat-form-field>
+
+  <mat-form-field *ngIf="combinedProperties.borderWidth !== undefined"
+                  appearance="fill" class="mdInput textsingleline">
+    <mat-label>{{'propertiesPanel.borderWidth' | translate }}</mat-label>
+    <input matInput type="number" #borderWidth="ngModel" min="0"
+           [ngModel]="combinedProperties.borderWidth"
+           (ngModelChange)="updateModel.emit({ property: 'borderWidth',
+                                                     value: $event,
+                                                     isInputValid: borderWidth.valid && $event !== null })">
+  </mat-form-field>
 </div>
-- 
GitLab