From 68a7ef59477b3856c7dcd2acf8c91f623211e19a Mon Sep 17 00:00:00 2001
From: mechtelm <nicht@mehr.fragen>
Date: Thu, 11 Nov 2021 09:15:06 +0100
Subject: [PATCH] Add slider to editor

---
 package-lock.json                             |  1 -
 .../element-components/slider.component.ts    | 30 +++++++++++++++++++
 projects/common/id.service.ts                 |  1 +
 projects/common/models/slider-element.ts      | 18 +++++++++++
 projects/common/models/uI-element.ts          |  2 +-
 projects/common/shared.module.ts              | 10 ++++++-
 projects/common/util/element.factory.ts       |  7 +++++
 .../ui-element-toolbox.component.html         |  5 ++++
 .../element-properties.component.html         | 18 +++++++++++
 projects/editor/src/assets/i18n/de.json       |  3 ++
 10 files changed, 92 insertions(+), 3 deletions(-)
 create mode 100644 projects/common/element-components/slider.component.ts
 create mode 100644 projects/common/models/slider-element.ts

diff --git a/package-lock.json b/package-lock.json
index 881bbd7d2..711c57e8c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4,7 +4,6 @@
   "requires": true,
   "packages": {
     "": {
-      "name": "verona-modules-aspect",
       "dependencies": {
         "@angular/animations": "~12.2.7",
         "@angular/cdk": "^12.2.6",
diff --git a/projects/common/element-components/slider.component.ts b/projects/common/element-components/slider.component.ts
new file mode 100644
index 000000000..a917c3671
--- /dev/null
+++ b/projects/common/element-components/slider.component.ts
@@ -0,0 +1,30 @@
+import { Component, EventEmitter, Output } from '@angular/core';
+import { ElementComponent } from '../element-component.directive';
+import { SliderElement } from '../models/slider-element';
+
+@Component({
+  selector: 'app-slider',
+  template: `
+    <div [style.display]="'flex'"
+         [style.background-color]="elementModel.backgroundColor"
+         [style.width.%]="100"
+         [style.height.%]="100">
+      <div *ngIf="elementModel.showLabel">{{elementModel.minValue | number:'.0'}}</div>
+      <mat-slider
+              [style.width.%]="100"
+              [style.height.%]="100"
+              [max]="elementModel.maxValue"
+              [min]="elementModel.minValue">
+      </mat-slider>
+      <div *ngIf="elementModel.showLabel">{{elementModel.maxValue | number:'.0'}}</div>
+    </div>
+  `,
+  styles: [
+    '.dynamic-image{width: 100%; height: fit-content}',
+    '.static-image{ width: 100%; height: 100%; object-fit: contain}'
+  ]
+})
+export class SliderComponent extends ElementComponent {
+  elementModel!: SliderElement;
+
+}
diff --git a/projects/common/id.service.ts b/projects/common/id.service.ts
index 1ac383e16..b4e997701 100644
--- a/projects/common/id.service.ts
+++ b/projects/common/id.service.ts
@@ -16,6 +16,7 @@ export class IdService {
     video: 0,
     likert: 0,
     likert_row: 0,
+    slider: 0,
     'radio-group-images': 0,
     'drop-list': 0
   };
diff --git a/projects/common/models/slider-element.ts b/projects/common/models/slider-element.ts
new file mode 100644
index 000000000..ac1284674
--- /dev/null
+++ b/projects/common/models/slider-element.ts
@@ -0,0 +1,18 @@
+import { SurfaceUIElement } from '../interfaces/UIElementInterfaces';
+import { UIElement } from './uI-element';
+import { initSurfaceElement } from '../util/unit-interface-initializer';
+
+export class SliderElement extends UIElement implements SurfaceUIElement {
+  label: string = 'Zahlenstrahl';
+  minValue: number = 0;
+  maxValue: number = 100;
+  showLabel: boolean = true;
+
+  backgroundColor: string = 'transparent';
+
+  constructor(serializedElement: UIElement) {
+    super(serializedElement);
+    Object.assign(this, serializedElement);
+    Object.assign(this, initSurfaceElement(serializedElement));
+  }
+}
diff --git a/projects/common/models/uI-element.ts b/projects/common/models/uI-element.ts
index 4296b50c0..328eb6133 100644
--- a/projects/common/models/uI-element.ts
+++ b/projects/common/models/uI-element.ts
@@ -3,7 +3,7 @@ import { IdService } from '../id.service';
 import { LikertColumn, LikertRow } from '../interfaces/UIElementInterfaces';
 
 export type UIElementType = 'text' | 'button' | 'text-field' | 'text-area' | 'checkbox'
-| 'dropdown' | 'radio' | 'image' | 'audio' | 'video' | 'likert' | 'likert_row' | 'radio-group-images' | 'drop-list';
+| 'dropdown' | 'radio' | 'image' | 'audio' | 'video' | 'likert' | 'likert_row' | 'radio-group-images' | 'drop-list' | 'slider';
 export type InputElementValue = string[] | string | number | boolean | null;
 
 export interface ValueChangeElement {
diff --git a/projects/common/shared.module.ts b/projects/common/shared.module.ts
index 630a28e88..a5c2d985c 100644
--- a/projects/common/shared.module.ts
+++ b/projects/common/shared.module.ts
@@ -32,6 +32,7 @@ import { RadioButtonGroupComponent } from './element-components/radio-button-gro
 import { ImageComponent } from './element-components/image.component';
 import { VideoComponent } from './element-components/video.component';
 import { AudioComponent } from './element-components/audio.component';
+import { SliderComponent } from './element-components/slider.component';
 import { SafeResourceUrlPipe } from './element-components/pipes/safe-resource-url.pipe';
 import { InputBackgroundColorDirective } from './element-components/directives/input-background-color.directive';
 import { ErrorTransformPipe } from './element-components/pipes/error-transform.pipe';
@@ -43,6 +44,7 @@ import { LikertRadioButtonGroupComponent } from './element-components/compound-e
 import { Magnifier } from './element-components/magnifier.component';
 import { RadioGroupImagesComponent } from './element-components/compound-elements/radio-group-images.component';
 import { DropListComponent } from './element-components/compound-elements/drop-list.component';
+import { MAT_DATE_LOCALE } from '@angular/material/core';
 
 @NgModule({
   imports: [
@@ -70,6 +72,7 @@ import { DropListComponent } from './element-components/compound-elements/drop-l
     ImageComponent,
     AudioComponent,
     VideoComponent,
+    SliderComponent,
     RadioButtonGroupComponent,
     CheckboxComponent,
     DropdownComponent,
@@ -110,6 +113,7 @@ import { DropListComponent } from './element-components/compound-elements/drop-l
     ImageComponent,
     AudioComponent,
     VideoComponent,
+    SliderComponent,
     RadioButtonGroupComponent,
     CheckboxComponent,
     DropdownComponent,
@@ -118,6 +122,10 @@ import { DropListComponent } from './element-components/compound-elements/drop-l
     MatDialogModule,
     TranslateModule,
     SafeResourceHTMLPipe
-  ]
+  ],
+  providers:
+    [
+      { provide: MAT_DATE_LOCALE, useValue: 'de-DE' }
+    ]
 })
 export class SharedModule { }
diff --git a/projects/common/util/element.factory.ts b/projects/common/util/element.factory.ts
index 3cbafa044..8195801f6 100644
--- a/projects/common/util/element.factory.ts
+++ b/projects/common/util/element.factory.ts
@@ -27,6 +27,8 @@ import { RadioGroupImagesComponent } from '../element-components/compound-elemen
 import { RadioGroupImagesElement } from '../models/compound-elements/radio-group-images';
 import { DropListComponent } from '../element-components/compound-elements/drop-list.component';
 import { DropListElement } from '../models/compound-elements/drop-list';
+import { SliderElement } from '../models/slider-element';
+import {SliderComponent} from "../element-components/slider.component";
 
 export function createElement(elementModel: UIElement): UIElement {
   let newElement: UIElement;
@@ -70,6 +72,9 @@ export function createElement(elementModel: UIElement): UIElement {
     case 'drop-list':
       newElement = new DropListElement(elementModel);
       break;
+    case 'slider':
+      newElement = new SliderElement(elementModel);
+      break;
     default:
       throw new Error(`ElementType ${elementModel.type} not found!`);
   }
@@ -108,6 +113,8 @@ export function getComponentFactory(
       return componentFactoryResolver.resolveComponentFactory(RadioGroupImagesComponent);
     case 'drop-list':
       return componentFactoryResolver.resolveComponentFactory(DropListComponent);
+    case 'slider':
+      return componentFactoryResolver.resolveComponentFactory(SliderComponent);
     default:
       throw new Error('unknown element');
   }
diff --git a/projects/editor/src/app/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html b/projects/editor/src/app/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html
index ad0c9c393..4b0f29a76 100644
--- a/projects/editor/src/app/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html
+++ b/projects/editor/src/app/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html
@@ -64,6 +64,11 @@
         <mat-icon>ondemand_video</mat-icon>
         Video
       </button>
+      <button mat-raised-button (click)="addUIElement('slider')"
+              draggable="true" (dragstart)="$event.dataTransfer?.setData('elementType','slider')">
+        <mat-icon>spa</mat-icon>
+        Zahlenstrahl
+      </button>
     </div>
   </mat-tab>
   <mat-tab>
diff --git a/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.html b/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.html
index acdf682e7..76f3be521 100644
--- a/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.html
+++ b/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.html
@@ -212,6 +212,18 @@
                  [ngModel]="combinedProperties.minLength"
                  (ngModelChange)="updateModel('minLength', $event, minLength.valid)">
         </mat-form-field>
+        <mat-form-field *ngIf="combinedProperties.minValue !== undefined" appearance="fill">
+          <mat-label>{{'propertiesPanel.minValue' | translate }}</mat-label>
+          <input matInput type="number" #minValue="ngModel" min="0"
+                 [ngModel]="combinedProperties.minValue"
+                 (ngModelChange)="updateModel('minValue', $event, minValue.valid)">
+        </mat-form-field>
+        <mat-form-field *ngIf="combinedProperties.maxValue !== undefined" appearance="fill">
+          <mat-label>{{'propertiesPanel.maxValue' | translate }}</mat-label>
+          <input matInput type="number" #maxValue="ngModel" min="0"
+                 [ngModel]="combinedProperties.maxValue"
+                 (ngModelChange)="updateModel('maxValue', $event, maxValue.valid)">
+        </mat-form-field>
         <mat-form-field *ngIf="combinedProperties.minLength &&
                                    $any(combinedProperties.minLength) > 0"
                         appearance="fill">
@@ -425,6 +437,12 @@
           {{'propertiesPanel.onlyOneItem' | translate }}
         </mat-checkbox>
 
+        <mat-checkbox *ngIf="combinedProperties.showLabel !== undefined"
+                      [checked]="$any(combinedProperties.showLabel)"
+                      (change)="updateModel('showLabel', $event.checked)">
+          {{'propertiesPanel.showLabel' | translate }}
+        </mat-checkbox>
+
         <mat-divider></mat-divider>
 
         <button mat-raised-button class="element-button"
diff --git a/projects/editor/src/assets/i18n/de.json b/projects/editor/src/assets/i18n/de.json
index 8fdcea97f..e66e36171 100644
--- a/projects/editor/src/assets/i18n/de.json
+++ b/projects/editor/src/assets/i18n/de.json
@@ -88,8 +88,11 @@
     "false": "falsch",
     "appearance": "Aussehen",
     "minLength": "Minimallänge",
+    "minValue": "Minimalwert",
     "minLengthWarnMessage": "Minimalwert Warnmeldung",
     "maxLength": "Maximalwert",
+    "maxValue": "Maximalwert",
+    "showLabel": "Zeige Beschriftung",
     "maxLengthWarnMessage": "Maximalwert Warnmeldung",
     "pattern": "Muster",
     "patternWarnMessage": "Muster Warnmeldung",
-- 
GitLab