From 7f571ddf8d138666138a8514c37afd274309610b Mon Sep 17 00:00:00 2001
From: rhenck <richard.henck@iqb.hu-berlin.de>
Date: Fri, 30 Jun 2023 11:04:30 +0200
Subject: [PATCH] Refactor PositionProperties

- Remove dynamicPositioning from PositionProperties. This is not
knowledge that the element needs to know. In the few places it was used,
it was fixed or solved by asking the containing section about which
positioning is active.

- Fix and improve cloze child sizing. All can now have dynamic width and
height.

- Fix empty lines in ClozeDocument rendering

- Editor SelectionService now knows if a cloze child is selected. This
way the PropertiesPanel can show valid dimension inputs regardless of
the underlying section.
---
 docs/unit_definition_changelog.txt            | 12 ++---
 .../components/button/button.component.ts     |  7 +--
 .../toggle-button.component.ts                |  1 -
 .../cloze/cloze-child-overlay.component.ts    | 20 +++------
 .../cloze/cloze.component.ts                  | 10 ++---
 .../components/geometry/geometry.component.ts |  2 +-
 .../directives/compound-element.directive.ts  |  4 +-
 .../cloze-child-elements/text-field-simple.ts |  4 +-
 .../cloze-child-elements/toggle-button.ts     |  2 -
 projects/common/models/elements/element.ts    |  5 +--
 .../elements/input-elements/spell-correct.ts  |  4 +-
 .../elements/input-elements/text-area.ts      |  4 +-
 .../elements/input-elements/text-field.ts     |  4 +-
 .../elements/property-group-interfaces.ts     |  3 --
 projects/common/models/section.ts             |  1 -
 .../common/services/sanitization.service.ts   |  1 -
 projects/common/shared.module.ts              |  4 +-
 .../canvas/overlays/canvas-element-overlay.ts |  7 +--
 .../dimension-field-set.component.ts          | 45 +++++++++----------
 .../position-field-set.component.ts           | 16 ++++---
 .../src/app/services/selection.service.ts     | 14 +++---
 .../editor/src/app/services/unit.service.ts   | 10 +----
 .../drop-list-component-extension.ts          | 10 +++--
 .../drop-list-nodeview.component.ts           |  4 +-
 24 files changed, 88 insertions(+), 106 deletions(-)

diff --git a/docs/unit_definition_changelog.txt b/docs/unit_definition_changelog.txt
index 97dc15d75..171afeff6 100644
--- a/docs/unit_definition_changelog.txt
+++ b/docs/unit_definition_changelog.txt
@@ -73,11 +73,13 @@ iqb-aspect-definition@1.0.0
 - DropList: +allowReplacement
 - DragAndDropValueObject: -returnToOriginOnReplacement; originListID and originListIndex are now mandatory
 
-3.11.0
-- Element.position: margin properties now have a unit attached and are therefore an
-  object ({value: number; unit: string})
+3.12.0
+- PositionProperties:
+  - remove "dynamicPositioning"
+  - remove "fixedSize"
+  - remove "useMinHeight"
+  - margin properties now have a unit attached and are therefore an object ({value: number; unit: string})
 - Section.gridColumnSizes and Section.gridRowSizes now have a unit attached and are therefore an object
   ({value: number; unit: string})
-
-3.12.0
 - Likert: new property: stickyHeaders
+- ToggleButton: dynamicWidth removed
diff --git a/projects/common/components/button/button.component.ts b/projects/common/components/button/button.component.ts
index ef08ef853..6afb20ca3 100644
--- a/projects/common/components/button/button.component.ts
+++ b/projects/common/components/button/button.component.ts
@@ -50,9 +50,8 @@ import { ElementComponent } from '../../directives/element-component.directive';
     </button>
     <input
         *ngIf="elementModel.imageSrc" type="image"
+        class="image"
         [src]="elementModel.imageSrc | safeResourceUrl"
-        [class]="elementModel.position?.dynamicPositioning &&
-                 !elementModel.position?.fixedSize ? 'dynamic-image' : 'static-image'"
         [alt]="'imageNotFound' | translate"
         (click)="elementModel.action && elementModel.actionParam !== null?
            navigateTo.emit({
@@ -62,9 +61,7 @@ import { ElementComponent } from '../../directives/element-component.directive';
            false">
   `,
   styles: [
-    '.dynamic-image {width: 100%; height: fit-content;}',
-    '.static-image {max-width: 100%; max-height: 100%; display: grid;}', // grid: to prevent scrollbars
-    '.fill-container {width: 100%; height: 100%;}'
+    '.image {width: 100%; height: 100%; object-fit: contain;}'
   ]
 })
 export class ButtonComponent extends ElementComponent {
diff --git a/projects/common/components/compound-elements/cloze/cloze-child-elements/toggle-button.component.ts b/projects/common/components/compound-elements/cloze/cloze-child-elements/toggle-button.component.ts
index e41a5b0d4..fb2c16526 100644
--- a/projects/common/components/compound-elements/cloze/cloze-child-elements/toggle-button.component.ts
+++ b/projects/common/components/compound-elements/cloze/cloze-child-elements/toggle-button.component.ts
@@ -11,7 +11,6 @@ import { ToggleButtonElement } from 'common/models/elements/compound-elements/cl
                              [isDisabled]="elementModel.readOnly"
                              [value]="elementModel.value"
                              [vertical]="elementModel.verticalOrientation"
-                             [style.width]="elementModel.dynamicWidth ? 'unset' : '100%'"
                              [matTooltip]="elementFormControl.errors && elementFormControl.touched ?
                                            (elementFormControl.errors | errorTransform: elementModel) : ''"
                              [matTooltipClass]="'error-tooltip'"
diff --git a/projects/common/components/compound-elements/cloze/cloze-child-overlay.component.ts b/projects/common/components/compound-elements/cloze/cloze-child-overlay.component.ts
index 264277c74..684da873a 100644
--- a/projects/common/components/compound-elements/cloze/cloze-child-overlay.component.ts
+++ b/projects/common/components/compound-elements/cloze/cloze-child-overlay.component.ts
@@ -25,34 +25,26 @@ import { ValueChangeElement } from 'common/models/elements/element';
                                                    element.dimensions.minHeight + 'px' : null"
          [style.max-height]="!element.dimensions.isHeightFixed && element.dimensions.maxHeight ?
                                                    element.dimensions.maxHeight + 'px' : null"
-
          (click)="elementSelected.emit(this); $event.stopPropagation();">
       <aspect-text-field-simple *ngIf="element.type === 'text-field-simple'" #childComponent
                                 [style.pointer-events]="editorMode ? 'none' : 'auto'"
                                 [parentForm]="parentForm"
-                                [elementModel]="$any(element)"
-                                [style.width]="element.dimensions.isWidthFixed ? element.dimensions.width+'px' : 'unset'"
-                                [style.height.px]="element.dimensions.height">
+                                [elementModel]="$any(element)">
       </aspect-text-field-simple>
       <aspect-drop-list *ngIf="element.type === 'drop-list'" #childComponent
                                [clozeContext]="true"
                                [style.pointer-events]="editorMode ? 'none' : 'auto'"
                                [parentForm]="parentForm"
-                               [elementModel]="$any(element)"
-                               >
+                               [elementModel]="$any(element)">
       </aspect-drop-list>
       <aspect-toggle-button *ngIf="element.type === 'toggle-button'" #childComponent
                             [style.pointer-events]="editorMode ? 'none' : 'auto'"
                             [parentForm]="parentForm"
-                            [elementModel]="$any(element)"
-                            [style.width]="element.dimensions.isWidthFixed ? element.dimensions.width+'px' : 'unset'"
-                            [style.height.px]="element.dimensions.height">
+                            [elementModel]="$any(element)">
       </aspect-toggle-button>
       <aspect-button *ngIf="element.type === 'button'" #childComponent
                      [style.pointer-events]="editorMode ? 'none' : 'auto'"
-                     [elementModel]="$any(element)"
-                     [style.width.px]="element.dimensions.width"
-                     [style.height.px]="element.dimensions.height">
+                     [elementModel]="$any(element)">
       </aspect-button>
     </div>
   `,
@@ -60,13 +52,13 @@ import { ValueChangeElement } from 'common/models/elements/element';
     ':host div > * {display: block;}'
   ]
 })
-export class CompoundChildOverlayComponent { // TODO rename to ClozeChildOverlay
+export class ClozeChildOverlay {
   @Input() element!: ToggleButtonElement | TextFieldSimpleElement | DropListElement;
   @Input() parentForm!: UntypedFormGroup;
   @Input() editorMode: boolean = false;
   @Input() lineHeight!: number;
   @Output() elementValueChanged = new EventEmitter<ValueChangeElement>();
-  @Output() elementSelected = new EventEmitter<CompoundChildOverlayComponent>();
+  @Output() elementSelected = new EventEmitter<ClozeChildOverlay>();
   @ViewChild('childComponent') childComponent!: ElementComponent;
 
   isSelected: boolean = false;
diff --git a/projects/common/components/compound-elements/cloze/cloze.component.ts b/projects/common/components/compound-elements/cloze/cloze.component.ts
index 55f3f4889..c44faf336 100644
--- a/projects/common/components/compound-elements/cloze/cloze.component.ts
+++ b/projects/common/components/compound-elements/cloze/cloze.component.ts
@@ -4,7 +4,7 @@ import {
 import { CompoundElementComponent } from 'common/directives/compound-element.directive';
 import { ElementComponent } from 'common/directives/element-component.directive';
 import { ClozeElement } from 'common/models/elements/compound-elements/cloze/cloze';
-import { CompoundChildOverlayComponent } from './compound-child-overlay.component';
+import { ClozeChildOverlay } from './cloze-child-overlay.component';
 
 // TODO background color implementieren
 @Component({
@@ -173,7 +173,7 @@ import { CompoundChildOverlayComponent } from './compound-child-overlay.componen
   styles: [
     'p {margin: 0}',
     ':host ::ng-deep p strong {letter-spacing: 0.04em; font-weight: 600;}', // bold less bold
-    ':host ::ng-deep p:empty::after {content: "\00A0"}', // render empty p
+    '::ng-deep p:empty::after {content: \'\'; display: inline-block;}', // render empty p
     'p span {font-size: inherit}',
     'sup, sub {line-height: 0;}',
     '.droplist-child {vertical-align: middle;}'
@@ -181,12 +181,12 @@ import { CompoundChildOverlayComponent } from './compound-child-overlay.componen
 })
 export class ClozeComponent extends CompoundElementComponent {
   @Input() elementModel!: ClozeElement;
-  @Output() childElementSelected = new EventEmitter<CompoundChildOverlayComponent>();
-  @ViewChildren(CompoundChildOverlayComponent) compoundChildren!: QueryList<CompoundChildOverlayComponent>;
+  @Output() childElementSelected = new EventEmitter<ClozeChildOverlay>();
+  @ViewChildren(ClozeChildOverlay) compoundChildren!: QueryList<ClozeChildOverlay>;
 
   editorMode: boolean = false;
 
   getFormElementChildrenComponents(): ElementComponent[] {
-    return this.compoundChildren.map((child: CompoundChildOverlayComponent) => child.childComponent);
+    return this.compoundChildren.map((child: ClozeChildOverlay) => child.childComponent);
   }
 }
diff --git a/projects/common/components/geometry/geometry.component.ts b/projects/common/components/geometry/geometry.component.ts
index 4c235d926..b82f39fab 100644
--- a/projects/common/components/geometry/geometry.component.ts
+++ b/projects/common/components/geometry/geometry.component.ts
@@ -16,7 +16,7 @@ declare const GGBApplet: any;
     <div class="geogebra-container"
          [style.height.px]="elementModel.height"
          [style.width.px]="elementModel.width"
-         [class.center]="this.elementModel.position.fixedSize && this.elementModel.position.dynamicPositioning">
+         [class.center]="this.elementModel.dimensions.isWidthFixed">
       <div [id]="elementModel.id" class="geogebra-applet"></div>
       <button *ngIf="this.elementModel.showResetIcon"
               mat-icon-button
diff --git a/projects/common/directives/compound-element.directive.ts b/projects/common/directives/compound-element.directive.ts
index 59fd6dcc3..03b1438ee 100644
--- a/projects/common/directives/compound-element.directive.ts
+++ b/projects/common/directives/compound-element.directive.ts
@@ -4,7 +4,7 @@ import {
 } from '@angular/core';
 import { UntypedFormGroup } from '@angular/forms';
 import { ElementComponent } from './element-component.directive';
-import { CompoundChildOverlayComponent } from '../components/compound-elements/cloze/compound-child-overlay.component';
+import { ClozeChildOverlay } from '../components/compound-elements/cloze/cloze-child-overlay.component';
 import { LikertRadioButtonGroupComponent } from
   '../components/compound-elements/likert/likert-radio-button-group.component';
 
@@ -12,7 +12,7 @@ import { LikertRadioButtonGroupComponent } from
 export abstract class CompoundElementComponent extends ElementComponent implements AfterViewInit {
   @Output() childrenAdded = new EventEmitter<ElementComponent[]>();
   @Input() parentForm!: UntypedFormGroup;
-  compoundChildren!: QueryList<CompoundChildOverlayComponent | LikertRadioButtonGroupComponent>;
+  compoundChildren!: QueryList<ClozeChildOverlay | LikertRadioButtonGroupComponent>;
 
   ngAfterViewInit(): void {
     this.childrenAdded.emit(this.getFormElementChildrenComponents());
diff --git a/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/text-field-simple.ts b/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/text-field-simple.ts
index 178775477..a0702e272 100644
--- a/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/text-field-simple.ts
+++ b/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/text-field-simple.ts
@@ -6,7 +6,7 @@ import { ElementComponent } from 'common/directives/element-component.directive'
 import {
   TextFieldSimpleComponent
 } from 'common/components/compound-elements/cloze/cloze-child-elements/text-field-simple.component';
-import { BasicStyles } from 'common/models/elements/property-group-interfaces';
+import { BasicStyles, DimensionProperties } from 'common/models/elements/property-group-interfaces';
 
 import { AnswerScheme } from 'common/models/elements/answer-scheme-interfaces';
 
@@ -24,7 +24,7 @@ export class TextFieldSimpleElement extends TextInputElement {
   };
 
   constructor(element: Partial<TextFieldSimpleElement>) {
-    super({ dimensions: { width: 150, height: 30 }, ...element });
+    super({ dimensions: { width: 150, height: 30, isWidthFixed: true } as DimensionProperties, ...element });
     if (element.minLength) this.minLength = element.minLength;
     if (element.minLengthWarnMessage !== undefined) this.minLengthWarnMessage = element.minLengthWarnMessage;
     if (element.maxLength) this.maxLength = element.maxLength;
diff --git a/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/toggle-button.ts b/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/toggle-button.ts
index a20ab301c..6e3c5ddd2 100644
--- a/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/toggle-button.ts
+++ b/projects/common/models/elements/compound-elements/cloze/cloze-child-elements/toggle-button.ts
@@ -15,7 +15,6 @@ export class ToggleButtonElement extends InputElement {
   strikeOtherOptions: boolean = false;
   strikeSelectedOption: boolean = false;
   verticalOrientation: boolean = false;
-  dynamicWidth: boolean = true;
   styling: BasicStyles & {
     lineHeight: number;
     selectionColor: string;
@@ -27,7 +26,6 @@ export class ToggleButtonElement extends InputElement {
     if (element.strikeOtherOptions) this.strikeOtherOptions = element.strikeOtherOptions;
     if (element.strikeSelectedOption) this.strikeSelectedOption = element.strikeSelectedOption;
     if (element.verticalOrientation) this.verticalOrientation = element.verticalOrientation;
-    if (element.dynamicWidth !== undefined) this.dynamicWidth = element.dynamicWidth;
     this.styling = {
       ...UIElement.initStylingProps({
         lineHeight: 100,
diff --git a/projects/common/models/elements/element.ts b/projects/common/models/elements/element.ts
index 6c037ec51..0f70fff12 100644
--- a/projects/common/models/elements/element.ts
+++ b/projects/common/models/elements/element.ts
@@ -92,11 +92,8 @@ export abstract class UIElement {
   static initPositionProps(properties: Partial<PositionProperties> = {}): PositionProperties {
     const defaults = UIElement.sanitizePositionProps(properties);
     return {
-      fixedSize: defaults.fixedSize !== undefined ? defaults.fixedSize as boolean : false,
-      dynamicPositioning: defaults.dynamicPositioning !== undefined ? defaults.dynamicPositioning as boolean : true,
       xPosition: defaults.xPosition !== undefined ? defaults.xPosition as number : 0,
       yPosition: defaults.yPosition !== undefined ? defaults.yPosition as number : 0,
-      useMinHeight: defaults.useMinHeight !== undefined ? defaults.useMinHeight as boolean : false,
       gridColumn: defaults.gridColumn !== undefined ? defaults.gridColumn as number : null,
       gridColumnRange: defaults.gridColumnRange !== undefined ? defaults.gridColumnRange as number : 1,
       gridRow: defaults.gridRow !== undefined ? defaults.gridRow as number : null,
@@ -193,7 +190,7 @@ export abstract class TextInputElement extends InputElement {
   showSoftwareKeyboard: boolean = false;
   softwareKeyboardShowFrench: boolean = false;
 
-  protected constructor(element: Record<string, any>) {
+  protected constructor(element: Partial<TextInputElement>) {
     super(element);
     if (element.inputAssistancePreset) this.inputAssistancePreset = element.inputAssistancePreset;
     if (element.inputAssistanceCustomKeys !== undefined) {
diff --git a/projects/common/models/elements/input-elements/spell-correct.ts b/projects/common/models/elements/input-elements/spell-correct.ts
index 626438595..35b13538f 100644
--- a/projects/common/models/elements/input-elements/spell-correct.ts
+++ b/projects/common/models/elements/input-elements/spell-correct.ts
@@ -5,14 +5,14 @@ import {
 import { ElementComponent } from 'common/directives/element-component.directive';
 import { SpellCorrectComponent } from 'common/components/input-elements/spell-correct.component';
 import { AnswerScheme } from 'common/models/elements/answer-scheme-interfaces';
-import { BasicStyles, PositionProperties } from 'common/models/elements/property-group-interfaces';
+import { BasicStyles, DimensionProperties, PositionProperties } from 'common/models/elements/property-group-interfaces';
 
 export class SpellCorrectElement extends TextInputElement implements PositionedUIElement {
   position: PositionProperties;
   styling: BasicStyles;
 
   constructor(element: Partial<SpellCorrectElement>) {
-    super({ dimensions: { width: 230, height: 80 }, ...element });
+    super({ dimensions: { width: 230, height: 80 } as DimensionProperties, ...element });
     this.position = UIElement.initPositionProps(element.position);
     this.styling = {
       ...UIElement.initStylingProps({ backgroundColor: 'transparent', ...element.styling })
diff --git a/projects/common/models/elements/input-elements/text-area.ts b/projects/common/models/elements/input-elements/text-area.ts
index 4ba95f8ef..51f0945bc 100644
--- a/projects/common/models/elements/input-elements/text-area.ts
+++ b/projects/common/models/elements/input-elements/text-area.ts
@@ -6,7 +6,7 @@ import { ElementComponent } from 'common/directives/element-component.directive'
 import { TextAreaComponent } from 'common/components/input-elements/text-area.component';
 
 import { AnswerScheme } from 'common/models/elements/answer-scheme-interfaces';
-import { BasicStyles, PositionProperties } from 'common/models/elements/property-group-interfaces';
+import { BasicStyles, DimensionProperties, PositionProperties } from 'common/models/elements/property-group-interfaces';
 
 export class TextAreaElement extends TextInputElement implements PositionedUIElement {
   appearance: 'fill' | 'outline' = 'outline';
@@ -22,7 +22,7 @@ export class TextAreaElement extends TextInputElement implements PositionedUIEle
   };
 
   constructor(element: Partial<TextAreaElement>) {
-    super({ dimensions: { width: 230, height: 132 }, ...element });
+    super({ dimensions: { width: 230, height: 132 } as DimensionProperties, ...element });
     if (element.appearance) this.appearance = element.appearance;
     if (element.resizeEnabled) this.resizeEnabled = element.resizeEnabled;
     if (element.rowCount) this.rowCount = element.rowCount;
diff --git a/projects/common/models/elements/input-elements/text-field.ts b/projects/common/models/elements/input-elements/text-field.ts
index 617603cde..cad56c34f 100644
--- a/projects/common/models/elements/input-elements/text-field.ts
+++ b/projects/common/models/elements/input-elements/text-field.ts
@@ -5,7 +5,7 @@ import {
 import { ElementComponent } from 'common/directives/element-component.directive';
 import { TextFieldComponent } from 'common/components/input-elements/text-field.component';
 import { AnswerScheme } from 'common/models/elements/answer-scheme-interfaces';
-import { BasicStyles, PositionProperties } from 'common/models/elements/property-group-interfaces';
+import { BasicStyles, DimensionProperties, PositionProperties } from 'common/models/elements/property-group-interfaces';
 
 export class TextFieldElement extends TextInputElement implements PositionedUIElement {
   appearance: 'fill' | 'outline' = 'outline';
@@ -24,7 +24,7 @@ export class TextFieldElement extends TextInputElement implements PositionedUIEl
   };
 
   constructor(element: Partial<TextFieldElement>) {
-    super({ dimensions: { width: 180, height: 120 }, ...element });
+    super({ dimensions: { width: 180, height: 120 } as DimensionProperties, ...element });
     if (element.appearance) this.appearance = element.appearance;
     if (element.minLength !== undefined) this.minLength = element.minLength;
     if (element.minLengthWarnMessage !== undefined) this.minLengthWarnMessage = element.minLengthWarnMessage;
diff --git a/projects/common/models/elements/property-group-interfaces.ts b/projects/common/models/elements/property-group-interfaces.ts
index 9c647d4f0..b79157010 100644
--- a/projects/common/models/elements/property-group-interfaces.ts
+++ b/projects/common/models/elements/property-group-interfaces.ts
@@ -2,11 +2,8 @@ import { Measurement } from 'common/models/elements/element';
 
 export interface PositionProperties {
   [index: string]: unknown;
-  // fixedSize: boolean;
-  dynamicPositioning: boolean;
   xPosition: number;
   yPosition: number;
-  // useMinHeight: boolean;
   gridColumn: number | null;
   gridColumnRange: number;
   gridRow: number | null;
diff --git a/projects/common/models/section.ts b/projects/common/models/section.ts
index 753c6ab6f..f435bce31 100644
--- a/projects/common/models/section.ts
+++ b/projects/common/models/section.ts
@@ -48,7 +48,6 @@ export class Section {
   }
 
   addElement(element: PositionedUIElement): void {
-    element.position.dynamicPositioning = this.dynamicPositioning;
     this.elements.push(element);
   }
 
diff --git a/projects/common/services/sanitization.service.ts b/projects/common/services/sanitization.service.ts
index 9fc667edd..73095142c 100644
--- a/projects/common/services/sanitization.service.ts
+++ b/projects/common/services/sanitization.service.ts
@@ -223,7 +223,6 @@ export class SanitizationService {
     }
     return {
       ...element,
-      dynamicPositioning: sectionDynamicPositioning,
       gridColumn: element.gridColumn !== undefined ?
         element.gridColumn : element.gridColumnStart,
       gridColumnRange: element.gridColumnEnd - element.gridColumnStart,
diff --git a/projects/common/shared.module.ts b/projects/common/shared.module.ts
index 02afa6d1a..8da83915b 100644
--- a/projects/common/shared.module.ts
+++ b/projects/common/shared.module.ts
@@ -66,7 +66,7 @@ import {
 import { TextMarkingBarComponent } from './components/text/text-marking-bar/text-marking-bar.component';
 import { StyleMarksPipe } from './pipes/styleMarks.pipe';
 import { TextMarkingButtonComponent } from './components/text/text-marking-bar/text-marking-button.component';
-import { CompoundChildOverlayComponent } from './components/compound-elements/cloze/compound-child-overlay.component';
+import { ClozeChildOverlay } from './components/compound-elements/cloze/cloze-child-overlay.component';
 import { MarkListPipe } from './pipes/mark-list.pipe';
 import { IsDisabledDirective } from './directives/is-disabled.directive';
 import { GeometryComponent } from './components/geometry/geometry.component';
@@ -136,7 +136,7 @@ import { ReferenceListSnackbarComponent } from './services/message.service';
     TextMarkingBarComponent,
     StyleMarksPipe,
     TextMarkingButtonComponent,
-    CompoundChildOverlayComponent,
+    ClozeChildOverlay,
     MarkListPipe,
     IsDisabledDirective,
     GeometryComponent,
diff --git a/projects/editor/src/app/components/canvas/overlays/canvas-element-overlay.ts b/projects/editor/src/app/components/canvas/overlays/canvas-element-overlay.ts
index b0cdafa0c..3db77552e 100644
--- a/projects/editor/src/app/components/canvas/overlays/canvas-element-overlay.ts
+++ b/projects/editor/src/app/components/canvas/overlays/canvas-element-overlay.ts
@@ -7,8 +7,8 @@ import { takeUntil } from 'rxjs/operators';
 import { ElementComponent } from 'common/directives/element-component.directive';
 import { CompoundElementComponent } from 'common/directives/compound-element.directive';
 import { ClozeComponent } from 'common/components/compound-elements/cloze/cloze.component';
-import { CompoundChildOverlayComponent } from
-  'common/components/compound-elements/cloze/compound-child-overlay.component';
+import { ClozeChildOverlay } from
+    'common/components/compound-elements/cloze/cloze-child-overlay.component';
 import { UIElement } from 'common/models/elements/element';
 import { GeometryComponent } from 'common/components/geometry/geometry.component';
 import { GeometryElement } from 'common/models/elements/geometry/geometry';
@@ -59,8 +59,9 @@ export abstract class CanvasElementOverlay implements OnInit, OnDestroy {
       this.childComponent.location.nativeElement.style.pointerEvents = 'unset';
       this.childComponent.instance.childElementSelected
         .pipe(takeUntil(this.ngUnsubscribe))
-        .subscribe((elementSelectionEvent: CompoundChildOverlayComponent) => {
+        .subscribe((elementSelectionEvent: ClozeChildOverlay) => {
           this.selectionService.selectElement({ elementComponent: elementSelectionEvent, multiSelect: false });
+          this.selectionService.isClozeChildSelected = true;
         });
     }
 
diff --git a/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/dimension-field-set.component.ts b/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/dimension-field-set.component.ts
index aae2cfa89..9fef00a26 100644
--- a/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/dimension-field-set.component.ts
+++ b/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/dimension-field-set.component.ts
@@ -8,30 +8,10 @@ import { DimensionProperties, PositionProperties } from 'common/models/elements/
   template: `
     <fieldset>
       <legend>Dimensionen</legend>
-      <mat-checkbox *ngIf="dimensions.dynamicWidth !== undefined"
-                    [checked]="$any(dimensions.dynamicWidth)"
-                    (change)="updateDimensionProperty('dynamicWidth', $event.checked)">
-        {{'propertiesPanel.dynamicWidth' | translate }}
-      </mat-checkbox>
-
-      <ng-container *ngIf="!positionProperties?.dynamicPositioning">
-        <mat-form-field appearance="fill">
-          <mat-label>{{'propertiesPanel.width' | translate }}</mat-label>
-          <input matInput type="number" min="0"
-                 [ngModel]="dimensions.width"
-                 (ngModelChange)="updateDimensionProperty('width', $event)">
-        </mat-form-field>
-        <mat-form-field>
-          <mat-label>
-            {{'propertiesPanel.height' | translate }}
-          </mat-label>
-          <input matInput type="number" min="0"
-                 [ngModel]="dimensions.height"
-                 (ngModelChange)="updateDimensionProperty('height', $event)">
-        </mat-form-field>
-      </ng-container>
-
-      <ng-container *ngIf="positionProperties?.dynamicPositioning">
+      <ng-container *ngIf="unitService.unit.pages[selectionService.selectedPageIndex]
+                    .sections[selectionService.selectedPageSectionIndex].dynamicPositioning ||
+                    selectionService.isClozeChildSelected;
+                    else elseBlock">
         <mat-checkbox #fixedWidth [checked]="$any(dimensions.isWidthFixed)"
                       (change)="updateDimensionProperty('isWidthFixed', $event.checked)">
           {{'propertiesPanel.isWidthFixed' | translate }}
@@ -114,6 +94,23 @@ import { DimensionProperties, PositionProperties } from 'common/models/elements/
                  (ngModelChange)="updateDimensionProperty('maxHeight', $event)">
         </mat-form-field>
       </ng-container>
+
+      <ng-template #elseBlock>
+        <mat-form-field appearance="fill">
+          <mat-label>{{'propertiesPanel.width' | translate }}</mat-label>
+          <input matInput type="number" min="0"
+                 [ngModel]="dimensions.width"
+                 (ngModelChange)="updateDimensionProperty('width', $event)">
+        </mat-form-field>
+        <mat-form-field>
+          <mat-label>
+            {{'propertiesPanel.height' | translate }}
+          </mat-label>
+          <input matInput type="number" min="0"
+                 [ngModel]="dimensions.height"
+                 (ngModelChange)="updateDimensionProperty('height', $event)">
+        </mat-form-field>
+      </ng-template>
     </fieldset>
   `
 })
diff --git a/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/position-field-set.component.ts b/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/position-field-set.component.ts
index 556f75a6d..fc4a31a05 100644
--- a/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/position-field-set.component.ts
+++ b/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/position-field-set.component.ts
@@ -3,16 +3,21 @@ import {
 } from '@angular/core';
 import { UIElementValue } from 'common/models/elements/element';
 import { PositionProperties } from 'common/models/elements/property-group-interfaces';
+import { SelectionService } from 'editor/src/app/services/selection.service';
+import { UnitService } from 'editor/src/app/services/unit.service';
 
 @Component({
   selector: 'aspect-position-field-set',
   template: `
     <fieldset>
       <legend>Position</legend>
-      <div *ngIf="!positionProperties.dynamicPositioning; else elseBlock"
+      <div *ngIf="!unitService.unit
+                    .pages[selectionService.selectedPageIndex]
+                    .sections[selectionService.selectedPageSectionIndex]
+                    .dynamicPositioning;
+                    else elseBlock"
            class="flex-row">
-        <mat-form-field *ngIf="!positionProperties.dynamicPositioning &&
-                               positionProperties.xPosition !== undefined"
+        <mat-form-field *ngIf="positionProperties.xPosition !== undefined"
                         appearance="outline">
           <mat-label>{{'propertiesPanel.xPosition' | translate }}</mat-label>
           <input matInput type="number" #xPosition="ngModel" min="0"
@@ -20,8 +25,7 @@ import { PositionProperties } from 'common/models/elements/property-group-interf
                  (ngModelChange)="updateModel.emit(
                         { property: 'xPosition', value: $event, isInputValid: xPosition.valid && $event !== null })">
         </mat-form-field>
-        <mat-form-field *ngIf="!positionProperties.dynamicPositioning &&
-                                 positionProperties.yPosition !== undefined"
+        <mat-form-field *ngIf="positionProperties.yPosition !== undefined"
                         appearance="outline">
           <mat-label>{{'propertiesPanel.yPosition' | translate }}</mat-label>
           <input matInput type="number" #yPosition="ngModel" min="0"
@@ -130,4 +134,6 @@ export class PositionFieldSetComponent {
       value: UIElementValue,
       isInputValid?: boolean | null
     }>();
+
+  constructor(public unitService: UnitService, public selectionService: SelectionService) {}
 }
diff --git a/projects/editor/src/app/services/selection.service.ts b/projects/editor/src/app/services/selection.service.ts
index 3284e6a6c..77e6d6566 100644
--- a/projects/editor/src/app/services/selection.service.ts
+++ b/projects/editor/src/app/services/selection.service.ts
@@ -3,8 +3,8 @@ import { BehaviorSubject, Observable } from 'rxjs';
 import { UIElement } from 'common/models/elements/element';
 import { CanvasElementOverlay } from 'editor/src/app/components/canvas/overlays/canvas-element-overlay';
 import {
-  CompoundChildOverlayComponent
-} from 'common/components/compound-elements/cloze/compound-child-overlay.component';
+  ClozeChildOverlay
+} from 'common/components/compound-elements/cloze/cloze-child-overlay.component';
 
 @Injectable({
   providedIn: 'root'
@@ -13,8 +13,8 @@ export class SelectionService {
   selectedPageIndex: number = 0;
   selectedPageSectionIndex: number = 0;
   private _selectedElements!: BehaviorSubject<UIElement[]>;
-  selectedElementComponents: (CanvasElementOverlay | CompoundChildOverlayComponent)[] = [];
-  selectedCompoundChild: { element: UIElement, nativeElement: HTMLElement } | null = null;
+  selectedElementComponents: (CanvasElementOverlay | ClozeChildOverlay)[] = [];
+  isClozeChildSelected: boolean = false;
 
   constructor() {
     this._selectedElements = new BehaviorSubject([] as UIElement[]);
@@ -28,17 +28,18 @@ export class SelectionService {
     return this._selectedElements.value;
   }
 
-  selectElement(event: { elementComponent: CanvasElementOverlay | CompoundChildOverlayComponent; multiSelect: boolean }): void {
+  selectElement(event: { elementComponent: CanvasElementOverlay | ClozeChildOverlay; multiSelect: boolean }): void {
     if (!event.multiSelect) {
       this.clearElementSelection();
     }
+    this.isClozeChildSelected = false;
     this.selectedElementComponents.push(event.elementComponent);
     event.elementComponent.setSelected(true);
     this._selectedElements.next(this.selectedElementComponents.map(componentElement => componentElement.element));
   }
 
   clearElementSelection(): void {
-    this.selectedElementComponents.forEach((overlayComponent: CanvasElementOverlay | CompoundChildOverlayComponent) => {
+    this.selectedElementComponents.forEach((overlayComponent: CanvasElementOverlay | ClozeChildOverlay) => {
       overlayComponent.setSelected(false);
     });
     this.selectedElementComponents = [];
@@ -46,6 +47,7 @@ export class SelectionService {
   }
 
   selectPage(index: number) {
+    this.clearElementSelection();
     this.selectedPageIndex = index;
     this.selectedPageSectionIndex = 0;
   }
diff --git a/projects/editor/src/app/services/unit.service.ts b/projects/editor/src/app/services/unit.service.ts
index 0e79e9652..ee22950d0 100644
--- a/projects/editor/src/app/services/unit.service.ts
+++ b/projects/editor/src/app/services/unit.service.ts
@@ -241,7 +241,6 @@ export class UnitService {
     previousSection.elements = previousSection.elements.filter(element => !elements.includes(element));
     elements.forEach(element => {
       newSection.elements.push(element as PositionedUIElement);
-      (element as PositionedUIElement).position.dynamicPositioning = newSection.dynamicPositioning;
     });
     this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
   }
@@ -282,14 +281,7 @@ export class UnitService {
   }
 
   updateSectionProperty(section: Section, property: string, value: string | number | boolean | { value: number; unit: string }[]): void {
-    if (property === 'dynamicPositioning') {
-      section.dynamicPositioning = value as boolean;
-      section.elements.forEach((element: UIElement) => {
-        (element as PositionedUIElement).position.dynamicPositioning = value as boolean;
-      });
-    } else {
-      section.setProperty(property, value);
-    }
+    section.setProperty(property, value);
     this.elementPropertyUpdated.next();
     this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
   }
diff --git a/projects/editor/src/app/text-editor/angular-node-views/drop-list-component-extension.ts b/projects/editor/src/app/text-editor/angular-node-views/drop-list-component-extension.ts
index 032c6659e..44ba9c764 100644
--- a/projects/editor/src/app/text-editor/angular-node-views/drop-list-component-extension.ts
+++ b/projects/editor/src/app/text-editor/angular-node-views/drop-list-component-extension.ts
@@ -3,6 +3,7 @@ import { Node, mergeAttributes } from '@tiptap/core';
 import { AngularNodeViewRenderer } from 'ngx-tiptap';
 import { DropListNodeviewComponent } from './drop-list-nodeview.component';
 import { DropListElement } from 'common/models/elements/input-elements/drop-list';
+import { DimensionProperties } from 'common/models/elements/property-group-interfaces';
 
 const DropListComponentExtension = (injector: Injector): Node => {
   return Node.create({
@@ -16,9 +17,12 @@ const DropListComponentExtension = (injector: Injector): Node => {
           default: new DropListElement({
             type: 'drop-list',
             id: 'cloze-child-id-placeholder',
-            width: 150,
-            height: 30,
-            onlyOneItem: true
+            onlyOneItem: true,
+            dimensions: {
+              width: 150,
+              height: 30,
+              isWidthFixed: true
+            } as DimensionProperties
           })
         }
       };
diff --git a/projects/editor/src/app/text-editor/angular-node-views/drop-list-nodeview.component.ts b/projects/editor/src/app/text-editor/angular-node-views/drop-list-nodeview.component.ts
index ab50bfb7c..a646f2195 100644
--- a/projects/editor/src/app/text-editor/angular-node-views/drop-list-nodeview.component.ts
+++ b/projects/editor/src/app/text-editor/angular-node-views/drop-list-nodeview.component.ts
@@ -6,8 +6,8 @@ import { AngularNodeViewComponent } from 'ngx-tiptap';
   template: `
     <div [style.display]="'inline-block'"
          [style.vertical-align]="'middle'"
-         [style.width.px]="node.attrs.model.width"
-         [style.height.px]="node.attrs.model.height">
+         [style.width.px]="node.attrs.model.dimensions.width"
+         [style.height.px]="node.attrs.model.dimensions.height">
       <aspect-drop-list [elementModel]="node.attrs.model"
                         [matTooltip]="'ID: ' + node.attrs.model.id">
       </aspect-drop-list>
-- 
GitLab