From f63443afa76f7176ddb08f20907fd577713bcd86 Mon Sep 17 00:00:00 2001
From: rhenck <richard.henck@iqb.hu-berlin.de>
Date: Wed, 1 Jun 2022 22:51:45 +0200
Subject: [PATCH] Fix simple drop list properties panel

Now properly finds all normal and simple drop lists.
This is achieved by a new method of unit all it's sub-components, which
returns all contained elements with and optional type filter.

Also improve (still not very pretty) styling of the drop list props
panel.

#260 #259
---
 .../elements/compound-elements/cloze/cloze.ts |  2 +-
 projects/common/models/page.ts                |  5 ++
 projects/common/models/section.ts             | 10 ++-
 projects/common/models/unit.ts                |  9 ++-
 .../common/services/sanitization.service.ts   |  6 +-
 .../section-insert-dialog.component.ts        |  2 +-
 .../drop-list-properties.component.ts         | 64 ++++++++++---------
 .../editor/src/app/services/unit.service.ts   | 12 ++--
 8 files changed, 62 insertions(+), 48 deletions(-)

diff --git a/projects/common/models/elements/compound-elements/cloze/cloze.ts b/projects/common/models/elements/compound-elements/cloze/cloze.ts
index 4d13bf342..a90b6dc5b 100644
--- a/projects/common/models/elements/compound-elements/cloze/cloze.ts
+++ b/projects/common/models/elements/compound-elements/cloze/cloze.ts
@@ -102,7 +102,7 @@ export class ClozeElement extends CompoundElement implements PositionedUIElement
   }
 
   getChildElements(): UIElement[] {
-    return ClozeElement.getDocumentChildElements(this.documnent);
+    return ClozeElement.getDocumentChildElements(this.document);
   }
 
   static getDocumentChildElements(document: ClozeDocument): UIElement[] {
diff --git a/projects/common/models/page.ts b/projects/common/models/page.ts
index 54e905ca7..2119d3492 100644
--- a/projects/common/models/page.ts
+++ b/projects/common/models/page.ts
@@ -1,5 +1,6 @@
 import { Section } from 'common/models/section';
 import { IDManager } from 'common/util/id-manager';
+import { UIElement } from 'common/models/elements/element';
 
 export class Page {
   [index: string]: any;
@@ -22,4 +23,8 @@ export class Page {
     if (page?.alwaysVisibleAspectRatio) this.alwaysVisibleAspectRatio = page.alwaysVisibleAspectRatio;
     this.sections = page?.sections.map(section => new Section(section, idManager)) || [new Section()];
   }
+
+  getAllElements(elementType?: string): UIElement[] {
+    return this.sections.map(section => section.getAllElements(elementType)).flat();
+  }
 }
diff --git a/projects/common/models/section.ts b/projects/common/models/section.ts
index 2f543d89e..eca8a7555 100644
--- a/projects/common/models/section.ts
+++ b/projects/common/models/section.ts
@@ -88,7 +88,13 @@ export class Section {
   }
 
   /* Includes children of children, i.e. compound children. */
-  getAllChildElements(): UIElement[] {
-    return this.elements.map(element => [element, ...element.getChildElements()]).flat();
+  getAllElements(elementType?: string): UIElement[] {
+    let allElements: UIElement[] =
+      this.elements.map(element => [element, ...element.getChildElements()])
+        .flat();
+    if (elementType) {
+      allElements = allElements.filter(element => element.type === elementType);
+    }
+    return allElements;
   }
 }
diff --git a/projects/common/models/unit.ts b/projects/common/models/unit.ts
index 9aa6c8e00..f23bcd99d 100644
--- a/projects/common/models/unit.ts
+++ b/projects/common/models/unit.ts
@@ -1,14 +1,19 @@
 import packageJSON from '../../../package.json';
 import { Page } from 'common/models/page';
 import { IDManager } from 'common/util/id-manager';
+import { UIElement } from 'common/models/elements/element';
 
 export class Unit {
   type = 'aspect-unit-definition';
   version: string;
   pages: Page[] = [];
 
-  constructor(unit?: Unit, idManager?: IDManager) {
+  constructor(unit?: Partial<Unit>, idManager?: IDManager) {
     this.version = packageJSON.config.unit_definition_version;
-    this.pages = unit?.pages.map(page => new Page(page, idManager)) || [new Page()];
+    this.pages = unit?.pages?.map(page => new Page(page, idManager)) || [new Page()];
+  }
+
+  getAllElements(elementType?: string): UIElement[] {
+    return this.pages.map(page => page.getAllElements(elementType)).flat();
   }
 }
diff --git a/projects/common/services/sanitization.service.ts b/projects/common/services/sanitization.service.ts
index 3bc7b1680..fc622e7a8 100644
--- a/projects/common/services/sanitization.service.ts
+++ b/projects/common/services/sanitization.service.ts
@@ -45,10 +45,10 @@ export class SanitizationService {
     return SanitizationService.isVersionOlderThanCurrent(SanitizationService.unitDefinitionVersion);
   }
 
-  sanitizeUnitDefinition(unitDefinition: Unit): Unit {
+  sanitizeUnitDefinition(unitDefinition: Unit): Partial<Unit> {
     return {
       ...unitDefinition,
-      pages: unitDefinition.pages.map((page: Page) => this.sanitizePage(page))
+      pages: unitDefinition.pages.map((page: Page) => this.sanitizePage(page)) as Page[]
     };
   }
 
@@ -71,7 +71,7 @@ export class SanitizationService {
     return version[2] < SanitizationService.expectedUnitVersion[2];
   }
 
-  private sanitizePage(page: Page): Page {
+  private sanitizePage(page: Page): Partial<Page> {
     return {
       ...page,
       sections: page.sections.map((section: Section) => this.sanitizeSection(section))
diff --git a/projects/editor/src/app/components/dialogs/section-insert-dialog.component.ts b/projects/editor/src/app/components/dialogs/section-insert-dialog.component.ts
index 89384e45c..3fb0c7cda 100644
--- a/projects/editor/src/app/components/dialogs/section-insert-dialog.component.ts
+++ b/projects/editor/src/app/components/dialogs/section-insert-dialog.component.ts
@@ -58,7 +58,7 @@ export class SectionInsertDialogComponent {
     try {
       this.newSection = new Section(JSON.parse(pastedText));
 
-      if (this.findElementsWithDuplicateID(this.newSection.getAllChildElements()).length > 0) {
+      if (this.findElementsWithDuplicateID(this.newSection.getAllElements()).length > 0) {
         this.operationStatusText =
           this.translateService.instant('Doppelte IDs festgestellt. Weiter mit neu generierten IDs?');
         this.operationStatus = 'yellow';
diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/drop-list-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/drop-list-properties.component.ts
index 73589c077..83360045f 100644
--- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/drop-list-properties.component.ts
+++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/drop-list-properties.component.ts
@@ -15,49 +15,47 @@ import { IDManager } from 'common/util/id-manager';
   selector: 'aspect-drop-list-properties',
   template: `
     <fieldset *ngIf="combinedProperties.type === 'drop-list' ||
-                     combinedProperties.type === 'drop-list-sorting'">
+                     combinedProperties.type === 'drop-list-simple'">
       <legend>Ablegeliste</legend>
 
-      <mat-form-field disabled="true" *ngIf="combinedProperties.type === 'drop-list' ||
-                                           combinedProperties.type === 'drop-list-sorting'">
-        <ng-container>
-          <mat-label>{{'preset' | translate }}</mat-label>
-          <div class="drop-list" cdkDropList [cdkDropListData]="combinedProperties.value"
-               (cdkDropListDropped)="moveListValue($any($event))">
-            <div *ngFor="let value of $any(combinedProperties.value); let i = index" cdkDrag
-                 class="list-items" fxLayout="row" fxLayoutAlign="end center">
-              <div fxFlex="70" class="draggable-element-label">
-                {{value.stringValue}} ({{value.id}})
-              </div>
-              <img [src]="value.imgSrcValue"
-                   [style.object-fit]="'scale-down'"
-                   [style.height.px]="40">
-              <button mat-icon-button color="primary"
-                      (click)="editDropListOption(i)">
-                <mat-icon>build</mat-icon>
-              </button>
-              <button mat-icon-button color="primary"
-                      (click)="removeListValue('value', i)">
-                <mat-icon>clear</mat-icon>
-              </button>
+      <div class="value-list-container">
+        <mat-label>{{'preset' | translate }}</mat-label>
+        <div class="drop-list" cdkDropList [cdkDropListData]="combinedProperties.value"
+             (cdkDropListDropped)="moveListValue($any($event))">
+          <div *ngFor="let value of $any(combinedProperties.value); let i = index" cdkDrag
+               class="list-items" fxLayout="row" fxLayoutAlign="end center">
+            <div fxFlex="70" class="draggable-element-label">
+              {{value.stringValue}} ({{value.id}})
             </div>
+            <img [src]="value.imgSrcValue"
+                 [style.object-fit]="'scale-down'"
+                 [style.height.px]="40">
+            <button mat-icon-button color="primary"
+                    (click)="editDropListOption(i)">
+              <mat-icon>build</mat-icon>
+            </button>
+            <button mat-icon-button color="primary"
+                    (click)="removeListValue('value', i)">
+              <mat-icon>clear</mat-icon>
+            </button>
           </div>
-        </ng-container>
-        <div fxLayout="row" fxLayoutAlign="center center">
-          <textarea matInput type="text" #newValue rows="2"
+        </div>
+        <div fxLayout="row" fxLayoutAlign="center center" class="text-area-container">
+          <textarea matInput type="text"
+                    #newValue rows="2"
                     (keyup.enter)="addDropListOption(newValue.value); newValue.select()"></textarea>
           <button mat-icon-button
                   (click)="addDropListOption(newValue.value); newValue.select()">
             <mat-icon>add</mat-icon>
           </button>
         </div>
-      </mat-form-field>
+      </div>
 
-      <mat-form-field appearance="fill" *ngIf="combinedProperties.connectedTo !== null">
+      <mat-form-field appearance="fill" *ngIf="combinedProperties.connectedTo !== null"
+                      (click)="generateValidDropLists()">
         <mat-label>{{'propertiesPanel.connectedDropLists' | translate }}</mat-label>
         <mat-select multiple [ngModel]="combinedProperties.connectedTo"
-                    (ngModelChange)="toggleConnectedDropList($event)"
-                    (click)="generateValidDropLists()">
+                    (ngModelChange)="toggleConnectedDropList($event)">
           <mat-select-trigger>
             {{'propertiesPanel.connectedDropLists' | translate }} ({{combinedProperties.connectedTo.length}})
           </mat-select-trigger>
@@ -109,7 +107,11 @@ import { IDManager } from 'common/util/id-manager';
   styles: [
     'mat-form-field {width: 100%;}',
     '.draggable-element-label {overflow-wrap: anywhere;}',
-    'mat-select {height: 100%;}'
+    'mat-select {height: 100%;}',
+    '.text-area-container {background-color: lightgray; margin-bottom: 15px;}',
+    '.value-list-container {background-color: rgba(0,0,0,.04);}',
+    '.text-area-container button {border: 1px solid gray;}',
+    '.value-list-container mat-label {font-size: large;}'
   ]
 })
 export class DropListPropertiesComponent {
diff --git a/projects/editor/src/app/services/unit.service.ts b/projects/editor/src/app/services/unit.service.ts
index d73d4e57a..b61d0d22c 100644
--- a/projects/editor/src/app/services/unit.service.ts
+++ b/projects/editor/src/app/services/unit.service.ts
@@ -404,14 +404,10 @@ export class UnitService {
 
   /* Used by props panel to show available dropLists to connect */
   getDropListElementIDs(): string[] {
-    return this.unit.pages
-      .map(page => page.sections
-        .map(section => section.elements
-          .reduce((accumulator: any[], currentValue: any) => (
-            currentValue.type === 'drop-list' ? accumulator.concat(currentValue.id) : accumulator), [])
-          .flat()
-        )
-        .flat()).flat();
+    const allDropLists = [
+      ...this.unit.getAllElements('drop-list'),
+      ...this.unit.getAllElements('drop-list-simple')];
+    return allDropLists.map(dropList => dropList.id);
   }
 
   replaceSection(pageIndex: number, sectionIndex: number, newSection: Section): void {
-- 
GitLab