From e7ba988cd03e213737dd8d7188a134fb5d59d235 Mon Sep 17 00:00:00 2001 From: rhenck <richard.henck@iqb.hu-berlin.de> Date: Thu, 5 Aug 2021 16:19:19 +0200 Subject: [PATCH] [editor] Optimize component parameters and refactor unit service Also refactor unit service to use more it's own selection variable instead of using passed references for manipulating objects. --- .../canvas/canvas-element-overlay.ts | 21 +- .../canvas/page-canvas.component.html | 8 +- .../page-view/canvas/page-canvas.component.ts | 28 +- .../page-view/canvas/selection.service.ts | 50 -- .../page-view/page-view.component.ts | 5 +- .../element-properties.component.ts | 568 +++++++++--------- .../properties/page-properties.component.ts | 22 +- .../properties/properties.component.html | 10 +- .../properties/properties.component.ts | 16 +- .../section-properties.component.ts | 30 +- .../unit-view/unit-view.component.html | 2 +- projects/editor/src/app/unit.service.ts | 123 ++-- 12 files changed, 407 insertions(+), 476 deletions(-) delete mode 100644 projects/editor/src/app/components/unit-view/page-view/canvas/selection.service.ts diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/canvas-element-overlay.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/canvas-element-overlay.ts index a1ce7447d..c55db2dc6 100644 --- a/projects/editor/src/app/components/unit-view/page-view/canvas/canvas-element-overlay.ts +++ b/projects/editor/src/app/components/unit-view/page-view/canvas/canvas-element-overlay.ts @@ -1,16 +1,14 @@ import { + Directive, Input, ComponentFactoryResolver, ComponentRef, - Directive, - EventEmitter, HostListener, - Input, - Output, ViewChild, ViewContainerRef } from '@angular/core'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; -import { UnitUIElement } from '../../../../../../../common/unit'; +// eslint-disable-next-line import/no-cycle import { UnitService } from '../../../../unit.service'; +import { UnitUIElement } from '../../../../../../../common/unit'; import * as ComponentUtils from '../../../../../../../common/component-utils'; import { FormElementComponent } from '../../../../../../../common/form-element-component.directive'; import { ValueChangeElement } from '../../../../../../../common/form'; @@ -19,9 +17,6 @@ import { ElementComponent } from '../../../../../../../common/element-component. @Directive() export abstract class CanvasElementOverlay { @Input() element!: UnitUIElement; - @Output() elementSelected = new EventEmitter<{ - componentElement: CanvasElementOverlay, - multiSelect: boolean }>(); @ViewChild('elementContainer', { read: ViewContainerRef, static: true }) private elementContainer!: ViewContainerRef; selected = false; protected childComponent!: ComponentRef<ElementComponent>; @@ -57,7 +52,7 @@ export abstract class CanvasElementOverlay { if (!(event.target as Element).tagName.includes('input'.toUpperCase()) && !(event.target as Element).tagName.includes('textarea'.toUpperCase()) && event.key === 'Delete') { - this.unitService.deleteElement(this.element); + this.unitService.deleteSelectedElements(); } } @@ -67,13 +62,9 @@ export abstract class CanvasElementOverlay { click(event: MouseEvent): void { if (event.shiftKey) { - this.elementSelected.emit({ - componentElement: this, multiSelect: true - }); + this.unitService.selectElement({ componentElement: this, multiSelect: true }); } else { - this.elementSelected.emit({ - componentElement: this, multiSelect: false - }); + this.unitService.selectElement({ componentElement: this, multiSelect: false }); } } diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.html b/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.html index bff14d451..58eca749e 100644 --- a/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.html +++ b/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.html @@ -6,14 +6,8 @@ <div #section_component app-canvas-section class="section" *ngFor="let section of page.sections; let i = index" [section]="section" [sectionIndex]="i" - (click)="selectSection(i)" cdkDropList cdkDropListSortingDisabled - (cdkDropListDropped)="elementDropped($event)" [cdkDropListData]="section" - [ngStyle]="{ - border: i === (unitService.selectedPageSectionIndex | async) ? '1px solid': '1px dotted', - 'width.px': section.width, - 'height.px': section.height, - 'background-color': section.backgroundColor}"> + (cdkDropListDropped)="elementDropped($event)" [cdkDropListData]="section"> </div> </div> </div> diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.ts index d1af3d84a..7f1c83332 100644 --- a/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.ts +++ b/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.ts @@ -1,9 +1,6 @@ import { - Component, OnInit, OnDestroy, - Input, QueryList, ViewChildren + Component, Input, QueryList, ViewChildren } from '@angular/core'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { CdkDragDrop, transferArrayItem } from '@angular/cdk/drag-drop'; import { UnitPage, UnitPageSection } from '../../../../../../../common/unit'; import { UnitService } from '../../../../unit.service'; @@ -17,26 +14,12 @@ import { CanvasSectionComponent } from './canvas-section.component'; '.canvasBackground {background-color: lightgrey; padding:20px; height: 100%; overflow: auto;}' ] }) -export class PageCanvasComponent implements OnInit, OnDestroy { - @Input() pageIndex!: number; +export class PageCanvasComponent { + @Input() page!: UnitPage; @ViewChildren('section_component') canvasSections!: QueryList<CanvasSectionComponent>; - page!: UnitPage; - private ngUnsubscribe = new Subject<void>(); constructor(public unitService: UnitService) { } - ngOnInit(): void { - this.unitService.getPageObservable(this.pageIndex) - .pipe(takeUntil(this.ngUnsubscribe)) - .subscribe((page: UnitPage) => { - this.page = page; - }); - } - - selectSection(id: number): void { - this.unitService.updatePageSectionSelection(Number(id)); - } - elementDropped(event: CdkDragDrop<UnitPageSection>): void { const sourceItemModel = event.item.data; @@ -70,9 +53,4 @@ export class PageCanvasComponent implements OnInit, OnDestroy { const reduceFct = (accumulator: number, currentValue: UnitPageSection) => accumulator + currentValue.height; return this.page.sections.reduce(reduceFct, 0); } - - ngOnDestroy(): void { - this.ngUnsubscribe.next(); - this.ngUnsubscribe.complete(); - } } diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/selection.service.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/selection.service.ts deleted file mode 100644 index 21e0bb04d..000000000 --- a/projects/editor/src/app/components/unit-view/page-view/canvas/selection.service.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Injectable, OnDestroy } from '@angular/core'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { UnitService } from '../../../../unit.service'; -import { UnitUIElement } from '../../../../../../../common/unit'; -import { CanvasElementOverlay } from './canvas-element-overlay'; - -@Injectable({ - providedIn: 'root' -}) -export class SelectionService implements OnDestroy { // TODO selectionService - private ngUnsubscribe = new Subject<void>(); - selectedComponentElements: CanvasElementOverlay[] = []; - - elementSelected: Subject<UnitUIElement[]> = new Subject<UnitUIElement[]>(); - - constructor(private unitService: UnitService) { - this.unitService.selectedPageIndex - .pipe(takeUntil(this.ngUnsubscribe)) - .subscribe(() => { - this.clearSelection(); - }); - } - - getSelectedElements(): UnitUIElement[] { - return this.selectedComponentElements.map(element => element.element); - } - - selectElement(event: { componentElement: CanvasElementOverlay; multiSelect: boolean }): void { - if (!event.multiSelect) { - this.clearSelection(); - } - this.selectedComponentElements.push(event.componentElement); - event.componentElement.setSelected(true); // TODO direkt in der component? - - this.elementSelected.next(this.selectedComponentElements.map(componentElement => componentElement.element)); - } - - private clearSelection() { - this.selectedComponentElements.forEach((overlayComponent: CanvasElementOverlay) => { - overlayComponent.setSelected(false); - }); - this.selectedComponentElements = []; - } - - ngOnDestroy(): void { - this.ngUnsubscribe.next(); - this.ngUnsubscribe.complete(); - } -} diff --git a/projects/editor/src/app/components/unit-view/page-view/page-view.component.ts b/projects/editor/src/app/components/unit-view/page-view/page-view.component.ts index ec08a3851..ecf2c1a52 100644 --- a/projects/editor/src/app/components/unit-view/page-view/page-view.component.ts +++ b/projects/editor/src/app/components/unit-view/page-view/page-view.component.ts @@ -1,16 +1,17 @@ import { Component, Input } from '@angular/core'; +import { UnitPage } from '../../../../../../common/unit'; @Component({ selector: 'app-page-view', template: ` - <app-page-canvas [pageIndex]="pageIndex" fxLayout="column"></app-page-canvas> + <app-page-canvas [page]="page" fxLayout="column"></app-page-canvas> `, styles: [ 'app-page-canvas {height: 100%; display: block}' ] }) export class PageViewComponent { - @Input() pageIndex!: number; + @Input() page!: UnitPage; } diff --git a/projects/editor/src/app/components/unit-view/page-view/properties/element-properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties/element-properties.component.ts index 688de1735..65d9da57b 100644 --- a/projects/editor/src/app/components/unit-view/page-view/properties/element-properties.component.ts +++ b/projects/editor/src/app/components/unit-view/page-view/properties/element-properties.component.ts @@ -5,307 +5,307 @@ import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { UnitUIElement } from '../../../../../../../common/unit'; import { UnitService } from '../../../../unit.service'; -import { SelectionService } from '../canvas/selection.service'; @Component({ selector: 'app-element-properties', template: ` - <mat-tab-group mat-stretch-tabs dynamicHeight> - <mat-tab> - <ng-template mat-tab-label> - <mat-icon class="example-tab-icon">build</mat-icon> - </ng-template> - <div fxLayout="column"> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('id')"> - <mat-label>ID</mat-label> - <input matInput type="text" *ngIf="selectedElements.length === 1" [value]="combinedProperties.id" - (input)="updateModel('id', $any($event.target).value)"> - <input matInput type="text" disabled *ngIf="selectedElements.length > 1" [value]="'Muss eindeutig sein'"> - </mat-form-field> - - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('label')"> - <mat-label>Label</mat-label> - <input matInput type="text" [value]="combinedProperties.label" - (input)="updateModel('label', $any($event.target).value)"> - </mat-form-field> - - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('text')"> - <mat-label>Text</mat-label> - <textarea matInput type="text" cdkTextareaAutosize [value]="combinedProperties.text" - (input)="updateModel('text', $any($event.target).value)"> - </textarea> - </mat-form-field> - - <mat-form-field *ngIf="combinedProperties.type === 'text-field'"> - <mat-label>Vorbelegung</mat-label> - <input matInput type="text" - [value]="combinedProperties.value" - (input)="updateModel('value', $any($event.target).value)"> - </mat-form-field> - <section *ngIf="combinedProperties.type === 'checkbox'"> - Vorbelegung - <mat-button-toggle-group (change)="transformToBoolAndUpdateModel('value', $event.value)"> - <mat-button-toggle value="true">wahr</mat-button-toggle> - <mat-button-toggle value="false">falsch</mat-button-toggle> - <mat-button-toggle value="undefined">undefiniert</mat-button-toggle> - </mat-button-toggle-group> - </section> - <mat-form-field *ngIf="combinedProperties.type === 'dropdown' || combinedProperties.type === 'radio'" - appearance="fill"> - <mat-label>Vorbelegung</mat-label> - <mat-select (selectionChange)="updateModel('value', $event.value)"> - <mat-option>undefiniert</mat-option> - <mat-option *ngFor="let option of $any(combinedProperties).options; let i = index" [value]="i"> - {{option}} - </mat-option> - </mat-select> - </mat-form-field> - - <mat-divider></mat-divider> - - <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('required')" - [checked]="$any(combinedProperties.required)" - (change)="updateModel('required', $event.checked)"> - Pflichtfeld - </mat-checkbox> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('requiredWarnMessage')"> - <mat-label>Warnmeldung</mat-label> - <input matInput type="text" [value]="combinedProperties.requiredWarnMessage" - (input)="updateModel('requiredWarnMessage', $any($event.target).value)"> - </mat-form-field> - - <mat-divider></mat-divider> - - - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('min')"> - <mat-label>Minimalwert</mat-label> - <input matInput type="number" [value]="combinedProperties.min" - (input)="updateModel('min', $any($event.target).value)"> - </mat-form-field> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('min')"> - <mat-label>Warnmeldung</mat-label> - <input matInput type="text" [value]="combinedProperties.minWarnMessage" - (input)="updateModel('minWarnMessage', $any($event.target).value)"> - </mat-form-field> - - <mat-divider></mat-divider> - - - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('max')"> - <mat-label>Maximalwert</mat-label> - <input matInput type="number" [value]="combinedProperties.max" - (input)="updateModel('max', $any($event.target).value)"> - </mat-form-field> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('max')"> - <mat-label>Warnmeldung</mat-label> - <input matInput type="text" [value]="combinedProperties.maxWarnMessage" - (input)="updateModel('maxWarnMessage', $any($event.target).value)"> - </mat-form-field> - - <mat-form-field disabled="true" *ngIf="combinedProperties.hasOwnProperty('options')"> - <div *ngIf="combinedProperties.options !== undefined"> - <mat-label>Optionen</mat-label> - <!-- TODO reorder via droplist--> - <mat-list *ngFor="let option of $any(combinedProperties.options)"> - <mat-list-item>{{option}}</mat-list-item> - <mat-divider></mat-divider> - </mat-list> - </div> - <div class="newOptionElement" fxLayout="row" fxLayoutAlign="center center"> - <button mat-icon-button matPrefix - (click)="updateModel('options', newOption.value)"> - <mat-icon>add</mat-icon> - </button> - <input #newOption matInput type="text" placeholder="Optionstext"> - </div> - </mat-form-field> - - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('alignment')"> - <mat-label>Ausrichtung</mat-label> - <mat-select [value]="combinedProperties.alignment" - (change)="$any($event.target).value"> - <mat-option *ngFor="let option of ['row', 'column']" [value]="option"> - {{option}} - </mat-option> - </mat-select> - </mat-form-field> - - <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('resizeEnabled')" - [checked]="$any(combinedProperties.resizeEnabled)" - (change)="updateModel('resizeEnabled', $event.checked)"> - größenverstellbar - </mat-checkbox> - - <mat-form-field disabled="true" *ngIf="combinedProperties.hasOwnProperty('sentences')"> - <div *ngIf="combinedProperties.sentences !== undefined"> - <mat-label>Sätze</mat-label> - <mat-list *ngFor="let sentence of $any(combinedProperties.sentences)"> - <mat-list-item>{{sentence}}</mat-list-item> - <mat-divider></mat-divider> - </mat-list> - </div> - <div class="newOptionElement" fxLayout="row" fxLayoutAlign="center center"> - <button mat-icon-button matPrefix - (click)="updateModel('sentences', newOption.value)"> - <mat-icon>add</mat-icon> - </button> - <input #newOption matInput type="text" placeholder="Optionstext"> - </div> - </mat-form-field> - - </div> - <button mat-raised-button class="delete-element-button" (click)="deleteElement()"> - Element löschen - </button> - <button mat-raised-button class="duplicate-element-button" (click)="duplicateElement()"> - Element duplizieren - </button> - </mat-tab> - - <mat-tab> - <ng-template mat-tab-label> - <mat-icon class="example-tab-icon">format_shapes</mat-icon> - </ng-template> - <div fxLayout="column"> - - <ng-container *ngIf="!combinedProperties.dynamicPositioning; else elseBlock"> - <!-- <ng-container>--> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('width')"> - <mat-label>Breite</mat-label> - <input matInput type="number" [value]="combinedProperties.width" - (input)="updateModel('width', $any($event.target).value)"> + <ng-container *ngIf="selectedElements"> + <mat-tab-group mat-stretch-tabs dynamicHeight> + <mat-tab> + <ng-template mat-tab-label> + <mat-icon class="example-tab-icon">build</mat-icon> + </ng-template> + <div fxLayout="column"> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('id')"> + <mat-label>ID</mat-label> + <input matInput type="text" *ngIf="selectedElements.length === 1" [value]="combinedProperties.id" + (input)="updateModel('id', $any($event.target).value)"> + <input matInput type="text" disabled *ngIf="selectedElements.length > 1" [value]="'Muss eindeutig sein'"> </mat-form-field> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('height')"> - <mat-label>Hoehe</mat-label> - <input matInput type="number" [value]="combinedProperties.height" - (input)="updateModel('height', $any($event.target).value)"> + + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('label')"> + <mat-label>Label</mat-label> + <input matInput type="text" [value]="combinedProperties.label" + (input)="updateModel('label', $any($event.target).value)"> </mat-form-field> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('xPosition')"> - <mat-label>X Position</mat-label> - <input matInput type="number" [value]="combinedProperties.xPosition" - (input)="updateModel('xPosition', $any($event.target).value)"> + + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('text')"> + <mat-label>Text</mat-label> + <textarea matInput type="text" cdkTextareaAutosize [value]="combinedProperties.text" + (input)="updateModel('text', $any($event.target).value)"> + </textarea> </mat-form-field> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('yPosition')"> - <mat-label>Y Position</mat-label> - <input matInput type="number" [value]="combinedProperties.yPosition" - (input)="updateModel('yPosition', $any($event.target).value)"> + + <mat-form-field *ngIf="combinedProperties.type === 'text-field'"> + <mat-label>Vorbelegung</mat-label> + <input matInput type="text" + [value]="combinedProperties.value" + (input)="updateModel('value', $any($event.target).value)"> </mat-form-field> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('zIndex')"> - <mat-label>Z-Index</mat-label> - <input matInput type="number" [value]="combinedProperties.zIndex" - (input)="updateModel('zIndex', $any($event.target).value)" - matTooltip="Priorität beim Stapeln von Elementen. Der höhere Index erscheint vorne."> + <section *ngIf="combinedProperties.type === 'checkbox'"> + Vorbelegung + <mat-button-toggle-group (change)="transformToBoolAndUpdateModel('value', $event.value)"> + <mat-button-toggle value="true">wahr</mat-button-toggle> + <mat-button-toggle value="false">falsch</mat-button-toggle> + <mat-button-toggle value="undefined">undefiniert</mat-button-toggle> + </mat-button-toggle-group> + </section> + <mat-form-field *ngIf="combinedProperties.type === 'dropdown' || combinedProperties.type === 'radio'" + appearance="fill"> + <mat-label>Vorbelegung</mat-label> + <mat-select (selectionChange)="updateModel('value', $event.value)"> + <mat-option>undefiniert</mat-option> + <mat-option *ngFor="let option of $any(combinedProperties).options; let i = index" [value]="i"> + {{option}} + </mat-option> + </mat-select> </mat-form-field> - </ng-container> - <ng-template #elseBlock> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('width')"> - <mat-label>Mindestbreite</mat-label> - <input matInput type="number" [value]="combinedProperties.width" - (input)="updateModel('width', $any($event.target).value)"> + + <mat-divider></mat-divider> + + <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('required')" + [checked]="$any(combinedProperties.required)" + (change)="updateModel('required', $event.checked)"> + Pflichtfeld + </mat-checkbox> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('requiredWarnMessage')"> + <mat-label>Warnmeldung</mat-label> + <input matInput type="text" [value]="combinedProperties.requiredWarnMessage" + (input)="updateModel('requiredWarnMessage', $any($event.target).value)"> </mat-form-field> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('height')"> - <mat-label>Mindesthoehe</mat-label> - <input matInput type="number" [value]="combinedProperties.height" - (input)="updateModel('height', $any($event.target).value)"> + + <mat-divider></mat-divider> + + + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('min')"> + <mat-label>Minimalwert</mat-label> + <input matInput type="number" [value]="combinedProperties.min" + (input)="updateModel('min', $any($event.target).value)"> + </mat-form-field> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('min')"> + <mat-label>Warnmeldung</mat-label> + <input matInput type="text" [value]="combinedProperties.minWarnMessage" + (input)="updateModel('minWarnMessage', $any($event.target).value)"> </mat-form-field> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('gridColumnStart')"> - <mat-label>Startspalte</mat-label> - <input matInput type="number" [value]="combinedProperties.gridColumnStart" - (input)="updateModel('gridColumnStart', $any($event.target).value)"> + <mat-divider></mat-divider> + + + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('max')"> + <mat-label>Maximalwert</mat-label> + <input matInput type="number" [value]="combinedProperties.max" + (input)="updateModel('max', $any($event.target).value)"> + </mat-form-field> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('max')"> + <mat-label>Warnmeldung</mat-label> + <input matInput type="text" [value]="combinedProperties.maxWarnMessage" + (input)="updateModel('maxWarnMessage', $any($event.target).value)"> </mat-form-field> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('gridColumnEnd')"> - <mat-label>Endspalte</mat-label> - <input matInput type="number" [value]="combinedProperties.gridColumnEnd" - (input)="updateModel('gridColumnEnd', $any($event.target).value)"> + + <mat-form-field disabled="true" *ngIf="combinedProperties.hasOwnProperty('options')"> + <div *ngIf="combinedProperties.options !== undefined"> + <mat-label>Optionen</mat-label> + <!-- TODO reorder via droplist--> + <mat-list *ngFor="let option of $any(combinedProperties.options)"> + <mat-list-item>{{option}}</mat-list-item> + <mat-divider></mat-divider> + </mat-list> + </div> + <div class="newOptionElement" fxLayout="row" fxLayoutAlign="center center"> + <button mat-icon-button matPrefix + (click)="updateModel('options', newOption.value)"> + <mat-icon>add</mat-icon> + </button> + <input #newOption matInput type="text" placeholder="Optionstext"> + </div> </mat-form-field> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('gridRowStart')"> - <mat-label>Startzeile</mat-label> - <input matInput type="number" [value]="combinedProperties.gridRowStart" - (input)="updateModel('gridRowStart', $any($event.target).value)"> + + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('alignment')"> + <mat-label>Ausrichtung</mat-label> + <mat-select [value]="combinedProperties.alignment" + (change)="$any($event.target).value"> + <mat-option *ngFor="let option of ['row', 'column']" [value]="option"> + {{option}} + </mat-option> + </mat-select> </mat-form-field> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('gridRowEnd')"> - <mat-label>Endzeile</mat-label> - <input matInput type="number" [value]="combinedProperties.gridRowEnd" - (input)="updateModel('gridRowEnd', $any($event.target).value)"> + + <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('resizeEnabled')" + [checked]="$any(combinedProperties.resizeEnabled)" + (change)="updateModel('resizeEnabled', $event.checked)"> + größenverstellbar + </mat-checkbox> + + <mat-form-field disabled="true" *ngIf="combinedProperties.hasOwnProperty('sentences')"> + <div *ngIf="combinedProperties.sentences !== undefined"> + <mat-label>Sätze</mat-label> + <mat-list *ngFor="let sentence of $any(combinedProperties.sentences)"> + <mat-list-item>{{sentence}}</mat-list-item> + <mat-divider></mat-divider> + </mat-list> + </div> + <div class="newOptionElement" fxLayout="row" fxLayoutAlign="center center"> + <button mat-icon-button matPrefix + (click)="updateModel('sentences', newOption.value)"> + <mat-icon>add</mat-icon> + </button> + <input #newOption matInput type="text" placeholder="Optionstext"> + </div> </mat-form-field> + + </div> + <button mat-raised-button class="delete-element-button" (click)="deleteElement()"> + Element löschen + </button> + <button mat-raised-button class="duplicate-element-button" (click)="duplicateElement()"> + Element duplizieren + </button> + </mat-tab> + + <mat-tab> + <ng-template mat-tab-label> + <mat-icon class="example-tab-icon">format_shapes</mat-icon> + </ng-template> + <div fxLayout="column"> + + <ng-container *ngIf="!combinedProperties.dynamicPositioning; else elseBlock"> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('width')"> + <mat-label>Breite</mat-label> + <input matInput type="number" [value]="combinedProperties.width" + (input)="updateModel('width', $any($event.target).value)"> + </mat-form-field> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('height')"> + <mat-label>Hoehe</mat-label> + <input matInput type="number" [value]="combinedProperties.height" + (input)="updateModel('height', $any($event.target).value)"> + </mat-form-field> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('xPosition')"> + <mat-label>X Position</mat-label> + <input matInput type="number" [value]="combinedProperties.xPosition" + (input)="updateModel('xPosition', $any($event.target).value)"> + </mat-form-field> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('yPosition')"> + <mat-label>Y Position</mat-label> + <input matInput type="number" [value]="combinedProperties.yPosition" + (input)="updateModel('yPosition', $any($event.target).value)"> + </mat-form-field> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('zIndex')"> + <mat-label>Z-Index</mat-label> + <input matInput type="number" [value]="combinedProperties.zIndex" + (input)="updateModel('zIndex', $any($event.target).value)" + matTooltip="Priorität beim Stapeln von Elementen. Der höhere Index erscheint vorne."> + </mat-form-field> + </ng-container> + <ng-template #elseBlock> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('width')"> + <mat-label>Mindestbreite</mat-label> + <input matInput type="number" [value]="combinedProperties.width" + (input)="updateModel('width', $any($event.target).value)"> + </mat-form-field> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('height')"> + <mat-label>Mindesthoehe</mat-label> + <input matInput type="number" [value]="combinedProperties.height" + (input)="updateModel('height', $any($event.target).value)"> + </mat-form-field> + + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('gridColumnStart')"> + <mat-label>Startspalte</mat-label> + <input matInput type="number" [value]="combinedProperties.gridColumnStart" + (input)="updateModel('gridColumnStart', $any($event.target).value)"> + </mat-form-field> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('gridColumnEnd')"> + <mat-label>Endspalte</mat-label> + <input matInput type="number" [value]="combinedProperties.gridColumnEnd" + (input)="updateModel('gridColumnEnd', $any($event.target).value)"> + </mat-form-field> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('gridRowStart')"> + <mat-label>Startzeile</mat-label> + <input matInput type="number" [value]="combinedProperties.gridRowStart" + (input)="updateModel('gridRowStart', $any($event.target).value)"> + </mat-form-field> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('gridRowEnd')"> + <mat-label>Endzeile</mat-label> + <input matInput type="number" [value]="combinedProperties.gridRowEnd" + (input)="updateModel('gridRowEnd', $any($event.target).value)"> + </mat-form-field> + </ng-template> + <ng-container *ngIf="selectedElements.length > 1"> + Ausrichtung + <div class="alignment-button-group" fxLayout="row" fxLayoutAlign="center center" > + <button (click)="alignElements('left')"> + <mat-icon>align_horizontal_left</mat-icon> + </button> + <button (click)="alignElements('right')"> + <mat-icon>align_horizontal_right</mat-icon> + </button> + <button (click)="alignElements('top')"> + <mat-icon>align_vertical_top</mat-icon> + </button> + <button (click)="alignElements('bottom')"> + <mat-icon>align_vertical_bottom</mat-icon> + </button> + </div> + </ng-container> + </div> + </mat-tab> + + <mat-tab> + <ng-template mat-tab-label> + <mat-icon class="example-tab-icon">palette</mat-icon> </ng-template> - <ng-container *ngIf="selectedElements.length > 1"> - Ausrichtung - <div class="alignment-button-group" fxLayout="row" fxLayoutAlign="center center" > - <button (click)="alignElements('left')"> - <mat-icon>align_horizontal_left</mat-icon> - </button> - <button (click)="alignElements('right')"> - <mat-icon>align_horizontal_right</mat-icon> - </button> - <button (click)="alignElements('top')"> - <mat-icon>align_vertical_top</mat-icon> - </button> - <button (click)="alignElements('bottom')"> - <mat-icon>align_vertical_bottom</mat-icon> - </button> - </div> - </ng-container> - </div> - </mat-tab> - - <mat-tab> - <ng-template mat-tab-label> - <mat-icon class="example-tab-icon">palette</mat-icon> - </ng-template> - <div fxLayout="column"> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('backgroundColor')" - class="mdInput textsingleline"> - <mat-label>Hintergrundfarbe</mat-label> - <input matInput type="text" [value]="combinedProperties.backgroundColor" - (input)="updateModel('backgroundColor', $any($event.target).value)"> - </mat-form-field> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('fontColor')" - class="mdInput textsingleline"> - <mat-label>Schriftfarbe</mat-label> - <input matInput type="text" [value]="combinedProperties.fontColor" - (input)="updateModel('fontColor', $any($event.target).value)"> - </mat-form-field> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('font')" - class="mdInput textsingleline"> - <mat-label>Schriftart</mat-label> - <input matInput type="text" [value]="combinedProperties.font" - (input)="updateModel('font', $any($event.target).value)"> - </mat-form-field> - <mat-form-field *ngIf="combinedProperties.hasOwnProperty('fontSize')" - class="mdInput textsingleline"> - <mat-label>Schriftgröße</mat-label> - <input matInput type="text" [value]="combinedProperties.fontSize" - (input)="updateModel('fontSize', $any($event.target).value)"> - </mat-form-field> - - <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('bold')" - [checked]="$any(combinedProperties.bold)" - (change)="updateModel('bold', $event.checked)"> - Fett - </mat-checkbox> - <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('italic')" - [checked]="$any(combinedProperties.italic)" - (change)="updateModel('italic', $event.checked)"> - Kursiv - </mat-checkbox> - <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('underline')" - [checked]="$any(combinedProperties.underline)" - (change)="updateModel('underline', $event.checked)"> - Unterstrichen - </mat-checkbox> - </div> - </mat-tab> - </mat-tab-group> + <div fxLayout="column"> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('backgroundColor')" + class="mdInput textsingleline"> + <mat-label>Hintergrundfarbe</mat-label> + <input matInput type="text" [value]="combinedProperties.backgroundColor" + (input)="updateModel('backgroundColor', $any($event.target).value)"> + </mat-form-field> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('fontColor')" + class="mdInput textsingleline"> + <mat-label>Schriftfarbe</mat-label> + <input matInput type="text" [value]="combinedProperties.fontColor" + (input)="updateModel('fontColor', $any($event.target).value)"> + </mat-form-field> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('font')" + class="mdInput textsingleline"> + <mat-label>Schriftart</mat-label> + <input matInput type="text" [value]="combinedProperties.font" + (input)="updateModel('font', $any($event.target).value)"> + </mat-form-field> + <mat-form-field *ngIf="combinedProperties.hasOwnProperty('fontSize')" + class="mdInput textsingleline"> + <mat-label>Schriftgröße</mat-label> + <input matInput type="text" [value]="combinedProperties.fontSize" + (input)="updateModel('fontSize', $any($event.target).value)"> + </mat-form-field> + + <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('bold')" + [checked]="$any(combinedProperties.bold)" + (change)="updateModel('bold', $event.checked)"> + Fett + </mat-checkbox> + <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('italic')" + [checked]="$any(combinedProperties.italic)" + (change)="updateModel('italic', $event.checked)"> + Kursiv + </mat-checkbox> + <mat-checkbox *ngIf="combinedProperties.hasOwnProperty('underline')" + [checked]="$any(combinedProperties.underline)" + (change)="updateModel('underline', $event.checked)"> + Unterstrichen + </mat-checkbox> + </div> + </mat-tab> + </mat-tab-group> + </ng-container> ` }) export class ElementPropertiesComponent implements OnInit, OnDestroy { - @Input() selectedElements!: UnitUIElement[]; + selectedElements!: UnitUIElement[]; combinedProperties: Record<string, string | number | boolean | string[] | undefined> = {}; private ngUnsubscribe = new Subject<void>(); - constructor(public unitService: UnitService, private selectionService: SelectionService) { } + constructor(public unitService: UnitService) { } ngOnInit(): void { this.unitService.elementPropertyUpdated @@ -315,7 +315,7 @@ export class ElementPropertiesComponent implements OnInit, OnDestroy { this.createCombinedProperties(); } ); - this.selectionService.elementSelected + this.unitService.elementSelected .pipe(takeUntil(this.ngUnsubscribe)) .subscribe( (selectedElements: UnitUIElement[]) => { @@ -350,7 +350,7 @@ export class ElementPropertiesComponent implements OnInit, OnDestroy { } alignElements(direction: 'left' | 'right' | 'top' | 'bottom'): void { - this.unitService.alignElements(this.selectionService.getSelectedElements(), direction); + this.unitService.alignSelectedElements(direction); } /* button group always handles values as string and since we also want to handle undefined @@ -372,15 +372,11 @@ export class ElementPropertiesComponent implements OnInit, OnDestroy { } deleteElement(): void { - this.selectedElements.forEach((element: UnitUIElement) => { - this.unitService.deleteElement(element); - }); + this.unitService.deleteSelectedElements(); } duplicateElement(): void { - this.selectedElements.forEach((element: UnitUIElement) => { - this.unitService.duplicateElement(element); - }); + this.unitService.duplicateSelectedElements(); } ngOnDestroy(): void { diff --git a/projects/editor/src/app/components/unit-view/page-view/properties/page-properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties/page-properties.component.ts index f2de9aae3..730df013a 100644 --- a/projects/editor/src/app/components/unit-view/page-view/properties/page-properties.component.ts +++ b/projects/editor/src/app/components/unit-view/page-view/properties/page-properties.component.ts @@ -1,7 +1,9 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { MatCheckboxChange } from '@angular/material/checkbox'; import { UnitPage } from '../../../../../../../common/unit'; import { UnitService } from '../../../../unit.service'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; @Component({ selector: 'app-page-properties', @@ -26,14 +28,28 @@ import { UnitService } from '../../../../unit.service'; </div> ` }) -export class PagePropertiesComponent { - @Input() selectedPage!: UnitPage; +export class PagePropertiesComponent implements OnInit, OnDestroy { + selectedPage!: UnitPage; + private ngUnsubscribe = new Subject<void>(); constructor(private unitService: UnitService) { } + ngOnInit(): void { + this.unitService.selectedPage + .pipe(takeUntil(this.ngUnsubscribe)) + .subscribe((page: UnitPage) => { + this.selectedPage = page; + }); + } + setPageAlwaysVisible(event: MatCheckboxChange): void { if (!this.unitService.setPageAlwaysVisible(event.checked)) { event.source.checked = false; } } + + ngOnDestroy(): void { + this.ngUnsubscribe.next(); + this.ngUnsubscribe.complete(); + } } diff --git a/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.html b/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.html index f5c28f877..792f824ff 100644 --- a/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.html +++ b/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.html @@ -1,20 +1,20 @@ <mat-accordion multi> - <mat-expansion-panel [expanded]="!expandElement"> + <mat-expansion-panel [expanded]="!expandElementView"> <mat-expansion-panel-header> <mat-panel-title>Seite</mat-panel-title> </mat-expansion-panel-header> - <app-page-properties [selectedPage]="selectedPage"></app-page-properties> + <app-page-properties></app-page-properties> </mat-expansion-panel> <mat-expansion-panel> <mat-expansion-panel-header> <mat-panel-title>Seitenabschnitt</mat-panel-title> </mat-expansion-panel-header> - <app-section-properties [selectedPageSection]="selectedPageSection"></app-section-properties> + <app-section-properties></app-section-properties> </mat-expansion-panel> - <mat-expansion-panel [expanded]="expandElement"> + <mat-expansion-panel [expanded]="expandElementView"> <mat-expansion-panel-header> <mat-panel-title>Element</mat-panel-title> </mat-expansion-panel-header> - <app-element-properties [selectedElements]="selectedElements"></app-element-properties> + <app-element-properties></app-element-properties> </mat-expansion-panel> </mat-accordion> diff --git a/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.ts index f260f5c9e..83c5b5573 100644 --- a/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.ts +++ b/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.ts @@ -3,7 +3,6 @@ import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { UnitService } from '../../../../unit.service'; import { UnitPage, UnitPageSection, UnitUIElement } from '../../../../../../../common/unit'; -import { SelectionService } from '../canvas/selection.service'; @Component({ selector: 'app-properties', @@ -21,26 +20,21 @@ export class PropertiesComponent { selectedElements: UnitUIElement[] = []; selectedPage!: UnitPage; selectedPageSection!: UnitPageSection; - expandElement: boolean = false; + expandElementView: boolean = false; private ngUnsubscribe = new Subject<void>(); - constructor(public unitService: UnitService, private selectionService: SelectionService) { } + constructor(public unitService: UnitService) { } ngOnInit(): void { this.unitService.selectedPage .pipe(takeUntil(this.ngUnsubscribe)) .subscribe((page: UnitPage) => { this.selectedPage = page; - this.expandElement = false; + this.expandElementView = false; }); - this.unitService.selectedPageSection + this.unitService.elementSelected .pipe(takeUntil(this.ngUnsubscribe)) - .subscribe((pageSection: UnitPageSection) => { - this.selectedPageSection = pageSection; - }); - this.selectionService.elementSelected - .pipe(takeUntil(this.ngUnsubscribe)) - .subscribe(() => { this.expandElement = true; }); + .subscribe(() => { this.expandElementView = true; }); } ngOnDestroy(): void { diff --git a/projects/editor/src/app/components/unit-view/page-view/properties/section-properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties/section-properties.component.ts index 8d36bcb2c..34e3d16a9 100644 --- a/projects/editor/src/app/components/unit-view/page-view/properties/section-properties.component.ts +++ b/projects/editor/src/app/components/unit-view/page-view/properties/section-properties.component.ts @@ -1,11 +1,15 @@ -import { Component, Input } from '@angular/core'; +import { + Component, Input, OnDestroy, OnInit +} from '@angular/core'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { UnitPageSection } from '../../../../../../../common/unit'; import { UnitService } from '../../../../unit.service'; @Component({ selector: 'app-section-properties', template: ` - <div fxLayout="column"> + <div *ngIf="selectedPageSection" fxLayout="column"> <mat-form-field> <mat-label>Breite</mat-label> <input matInput type="number" [(ngModel)]="selectedPageSection.width"> @@ -19,7 +23,7 @@ import { UnitService } from '../../../../unit.service'; <input matInput type="text" [(ngModel)]="selectedPageSection.backgroundColor"> </mat-form-field> <mat-checkbox [checked]="selectedPageSection.dynamicPositioning" - (change)="unitService.setSectionDynamicPositioning(selectedPageSection, $event.checked)"> + (change)="unitService.setSectionDynamicPositioning($event.checked)"> dynamisches Layout </mat-checkbox> <mat-form-field> @@ -28,14 +32,28 @@ import { UnitService } from '../../../../unit.service'; </mat-form-field> </div> <button mat-raised-button - (click)="unitService.deleteSection()"> + (click)="unitService.deleteSelectedSection()"> Entfernen <mat-icon>remove</mat-icon> </button> ` }) -export class SectionPropertiesComponent { - @Input() selectedPageSection!: UnitPageSection; +export class SectionPropertiesComponent implements OnInit, OnDestroy { + selectedPageSection!: UnitPageSection; + private ngUnsubscribe = new Subject<void>(); constructor(public unitService: UnitService) { } + + ngOnInit(): void { + this.unitService.selectedPageSection + .pipe(takeUntil(this.ngUnsubscribe)) + .subscribe((pageSection: UnitPageSection) => { + this.selectedPageSection = pageSection; + }); + } + + ngOnDestroy(): void { + this.ngUnsubscribe.next(); + this.ngUnsubscribe.complete(); + } } diff --git a/projects/editor/src/app/components/unit-view/unit-view.component.html b/projects/editor/src/app/components/unit-view/unit-view.component.html index bd0a362f0..655cd2cc2 100644 --- a/projects/editor/src/app/components/unit-view/unit-view.component.html +++ b/projects/editor/src/app/components/unit-view/unit-view.component.html @@ -22,7 +22,7 @@ <mat-icon>delete</mat-icon> </button> </ng-template> - <app-page-view [pageIndex]="i"></app-page-view> + <app-page-view [page]="page"></app-page-view> </mat-tab> <mat-tab disabled> <ng-template mat-tab-label> diff --git a/projects/editor/src/app/unit.service.ts b/projects/editor/src/app/unit.service.ts index b00dc3d10..014a399bd 100644 --- a/projects/editor/src/app/unit.service.ts +++ b/projects/editor/src/app/unit.service.ts @@ -8,6 +8,10 @@ import * as UnitFactory from './model/UnitFactory'; import { MessageService } from '../../../common/message.service'; import { IdService } from './id.service'; import { DialogService } from './dialog.service'; +// eslint-disable-next-line import/no-cycle +import { CanvasElementOverlay } from './components/unit-view/page-view/canvas/canvas-element-overlay'; +// eslint-disable-next-line import/no-cycle +import { CanvasSectionComponent } from './components/unit-view/page-view/canvas/canvas-section.component'; @Injectable({ providedIn: 'root' @@ -16,13 +20,14 @@ export class UnitService { private _unit: BehaviorSubject<Unit>; private _selectedPage: BehaviorSubject<UnitPage>; private _selectedPageSection: BehaviorSubject<UnitPageSection>; + private selectedPageSectionComponent!: CanvasSectionComponent; private _selectedPageIndex: BehaviorSubject<number>; // TODO weg refactorn - private _pages: BehaviorSubject<UnitPage>[]; - - private _selectedPageSectionIndex: BehaviorSubject<number>; elementPropertyUpdated: Subject<void> = new Subject<void>(); + selectedComponentElements: CanvasElementOverlay[] = []; + elementSelected: Subject<UnitUIElement[]> = new Subject<UnitUIElement[]>(); + constructor(private messageService: MessageService, private idService: IdService, private dialogService: DialogService) { @@ -33,14 +38,38 @@ export class UnitService { initialUnit.pages.push(initialPage); this._unit = new BehaviorSubject(initialUnit); - this._pages = [new BehaviorSubject(initialPage as UnitPage)]; this._selectedPageIndex = new BehaviorSubject(0); this._selectedPage = new BehaviorSubject(initialPage); this._selectedPageSection = new BehaviorSubject(initialPage.sections[0]); + } - this._selectedPageSectionIndex = new BehaviorSubject<number>(0); + // == SELECTION =============================== + selectElement(event: { componentElement: CanvasElementOverlay; multiSelect: boolean }): void { + if (!event.multiSelect) { + this.clearSelection(); + } + this.selectedComponentElements.push(event.componentElement); + event.componentElement.setSelected(true); // TODO direkt in der component? + this.elementSelected.next(this.selectedComponentElements.map(componentElement => componentElement.element)); + } + + private clearSelection() { + this.selectedComponentElements.forEach((overlayComponent: CanvasElementOverlay) => { + overlayComponent.setSelected(false); + }); + this.selectedComponentElements = []; } + selectSection(sectionComponent: CanvasSectionComponent): void { + if (this.selectedPageSectionComponent) { + this.selectedPageSectionComponent.selected = false; + } + this.selectedPageSectionComponent = sectionComponent; + this.selectedPageSectionComponent.selected = true; + this._selectedPageSection.next(sectionComponent.section); + } + // =========================================== + get unit(): Observable<Unit> { return this._unit.asObservable(); } @@ -57,23 +86,10 @@ export class UnitService { return this._selectedPageIndex.asObservable(); } - get selectedPageSectionIndex(): Observable<number> { - return this._selectedPageSectionIndex.asObservable(); - } - - getSelectedPageSection(): UnitPageSection { - return this._unit.value.pages[this._selectedPageIndex.value].sections[this._selectedPageSectionIndex.value]; - } - - getPageObservable(index: number): Observable<UnitPage> { - return this._pages[index].asObservable(); - } - addPage(): void { const newPage = UnitFactory.createUnitPage(this._unit.value.pages.length); newPage.sections.push(UnitFactory.createUnitPageSection()); this._unit.value.pages.push(newPage); - this._pages.push(new BehaviorSubject(newPage as UnitPage)); this._unit.next(this._unit.value); this._selectedPageIndex.next(this._unit.value.pages.length - 1); @@ -82,8 +98,6 @@ export class UnitService { deletePage(index: number = this._selectedPageIndex.value): void { this._unit.value.pages.splice(index, 1); - this._pages.splice(index, 1); - this._unit.next(this._unit.value); if (index === this._selectedPageIndex.value) { this._selectedPageIndex.next(this._selectedPageIndex.value - 1); @@ -102,24 +116,18 @@ export class UnitService { } addSection(): void { - const newSection = UnitFactory.createUnitPageSection(); - this._unit.value.pages[this._selectedPageIndex.value].sections.push(newSection); + this._unit.value.pages[this._selectedPageIndex.value].sections.push(UnitFactory.createUnitPageSection()); this._unit.next(this._unit.value); - this._pages[this._selectedPageIndex.value].next(this._unit.value.pages[this._selectedPageIndex.value]); // TODO auslagern? } - deleteSection(): void { + deleteSelectedSection(): void { if (this._unit.value.pages[this._selectedPageIndex.value].sections.length < 2) { this.messageService.showWarning('cant delete last section'); } else { - const index = this._selectedPageSectionIndex.value; - this._unit.value.pages[this._selectedPageIndex.value].sections.splice(index, 1); + this._unit.value.pages[this._selectedPageIndex.value].sections.splice( + this._unit.value.pages[this._selectedPageIndex.value].sections.indexOf(this._selectedPageSection.value), 1 + ); this._unit.next(this._unit.value); - - this._pages[this._selectedPageIndex.value].next(this._unit.value.pages[this._selectedPageIndex.value]); - if (this._selectedPageSectionIndex.value > 0) { - this._selectedPageSectionIndex.next(this._selectedPageSectionIndex.value - 1); - } } } @@ -176,32 +184,25 @@ export class UnitService { throw new Error(`ElementType ${elementType} not found!`); } newElement.id = this.idService.getNewID(elementType); - newElement.dynamicPositioning = this._unit.value.pages[this._selectedPageIndex.value] - .sections[this._selectedPageSectionIndex.value].dynamicPositioning; - this._unit.value.pages[this._selectedPageIndex.value] - .sections[this._selectedPageSectionIndex.value].elements.push(newElement!); - - this._pages[this._selectedPageIndex.value].next(this._unit.value.pages[this._selectedPageIndex.value]); + newElement.dynamicPositioning = this._selectedPageSection.value.dynamicPositioning; + this._selectedPageSection.value.elements.push(newElement); } - deleteElement(elementToDelete: UnitUIElement): void { - const oldElements = this._unit.value.pages[this._selectedPageIndex.value] - .sections[this._selectedPageSectionIndex.value].elements; - this._unit.value.pages[this._selectedPageIndex.value] - .sections[this._selectedPageSectionIndex.value].elements = - oldElements.filter(element => element !== elementToDelete); - this._pages[this._selectedPageIndex.value].next(this._unit.value.pages[this._selectedPageIndex.value]); + deleteSelectedElements(): void { + const selectedElements = this.selectedComponentElements.map(componentElement => componentElement.element); + this._selectedPageSection.value.elements = + this._selectedPageSection.value.elements.filter(element => !selectedElements.includes(element)); } - duplicateElement(elementToDuplicate: UnitUIElement): void { - const newElement: UnitUIElement = { ...elementToDuplicate }; - newElement.id = this.idService.getNewID(newElement.type); - newElement.xPosition += 10; - newElement.yPosition += 10; - - this._unit.value.pages[this._selectedPageIndex.value] - .sections[this._selectedPageSectionIndex.value].elements.push(newElement); - this._pages[this._selectedPageIndex.value].next(this._unit.value.pages[this._selectedPageIndex.value]); + duplicateSelectedElements(): void { + const selectedElements = this.selectedComponentElements.map(componentElement => componentElement.element); + selectedElements.forEach((element: UnitUIElement) => { + const newElement: UnitUIElement = { ...element }; + newElement.id = this.idService.getNewID(newElement.type); + newElement.xPosition += 10; + newElement.yPosition += 10; + this._selectedPageSection.value.elements.push(newElement); + }); } updatePageSelection(newIndex: number): void { @@ -209,11 +210,6 @@ export class UnitService { this._selectedPage.next(this._unit.value.pages[newIndex]); } - updatePageSectionSelection(newIndex: number): void { - this._selectedPageSectionIndex.next(newIndex); - this._selectedPageSection.next(this._unit.value.pages[this._selectedPageIndex.value].sections[newIndex]); - } - updateElementProperty( element: UnitUIElement, property: string, value: string | number | boolean | undefined ): boolean { @@ -236,7 +232,8 @@ export class UnitService { return true; } - alignElements(elements: UnitUIElement[], alignmentDirection: 'left' | 'right' | 'top' | 'bottom'): void { + alignSelectedElements(alignmentDirection: 'left' | 'right' | 'top' | 'bottom'): void { + const elements = this.selectedComponentElements.map(componentElement => componentElement.element); let newValue: number; switch (alignmentDirection) { case 'left': @@ -268,7 +265,8 @@ export class UnitService { this.elementPropertyUpdated.next(); } - setSectionDynamicPositioning(section: UnitPageSection, value: boolean): void { + setSectionDynamicPositioning(value: boolean): void { + const section = this._selectedPageSection.value; section.dynamicPositioning = value; section.elements.forEach((element: UnitUIElement) => { element.dynamicPositioning = value; @@ -286,11 +284,6 @@ export class UnitService { this._selectedPageIndex.next(0); this._selectedPage.next(this._unit.value.pages[0]); this._unit.next(newUnit); - this._pages = []; - this._unit.value.pages.forEach((page: UnitPage) => { - this._pages.push(new BehaviorSubject(page)); - }); - this.idService.readExistingIDs(this._unit.value); } -- GitLab