From 683288f8d11f0ff22987569d0cfa926b15d65f82 Mon Sep 17 00:00:00 2001 From: rhenck <richard.henck@iqb.hu-berlin.de> Date: Wed, 3 Jul 2024 12:42:55 +0200 Subject: [PATCH] [editor] Refactor option adding and editing Allow for direct adding of image options. --- .../likert-row-edit-dialog.component.ts | 4 +- .../drop-list-properties.component.ts | 8 +- .../options-field-set.component.ts | 61 +++++++++--- .../option-list-panel.component.ts | 96 ++++++++++++++++--- 4 files changed, 138 insertions(+), 31 deletions(-) diff --git a/projects/editor/src/app/components/dialogs/likert-row-edit-dialog.component.ts b/projects/editor/src/app/components/dialogs/likert-row-edit-dialog.component.ts index f5da8d83e..9bfc77060 100644 --- a/projects/editor/src/app/components/dialogs/likert-row-edit-dialog.component.ts +++ b/projects/editor/src/app/components/dialogs/likert-row-edit-dialog.component.ts @@ -88,10 +88,10 @@ import { TextLabel } from 'common/models/elements/label-interfaces'; export class LikertRowEditDialogComponent { constructor(@Inject(MAT_DIALOG_DATA) public data: { row: LikertRowElement, options: TextLabel[] }) { } - newLikertRow = { + newLikertRow = new LikertRowElement({ ...this.data.row, rowLabel: { ...this.data.row.rowLabel } - }; + }); async loadImage(): Promise<void> { this.newLikertRow.rowLabel.imgSrc = await FileService.loadImage(); diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/drop-list-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/drop-list-properties.component.ts index cdd1069c7..d8000ca1e 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/drop-list-properties.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/drop-list-properties.component.ts @@ -55,10 +55,10 @@ export class GetValidDropListsPipe implements PipeTransform { class="fx-column-start-stretch"> <aspect-option-list-panel [title]="'preset'" [textFieldLabel]="'Neue Option'" [itemList]="$any(combinedProperties.value)" - (addItem)="addOption($event)" - (removeItem)="removeOption($event)" - (changedItemOrder)="moveOption('value', $event)" - (editItem)="editOption($event)"> + (textItemAdded)="addOption($event)" + (itemRemoved)="removeOption($event)" + (itemReordered)="moveOption('value', $event)" + (itemEdited)="editOption($event)"> </aspect-option-list-panel> <mat-form-field *ngIf="combinedProperties.connectedTo !== null" diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/options-field-set.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/options-field-set.component.ts index a0037ed7c..31920a2dc 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/options-field-set.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/options-field-set.component.ts @@ -18,24 +18,28 @@ import { ElementService } from 'editor/src/app/services/unit-services/element.se <!--dropdown, radio-button-group--> <!-- [useRichText]="combinedProperties.type === 'radio'"--> <aspect-option-list-panel *ngIf="combinedProperties.options !== undefined" + [combinedProperties]="combinedProperties" [title]="'propertiesPanel.options'" [textFieldLabel]="'Neue Option'" [itemList]="$any(combinedProperties.options)" - (addItem)="addOption('options', $event)" - (removeItem)="removeOption('options', $event)" - (changedItemOrder)="moveOption('options', $event)" - (editItem)="editOption('options', $event)"> + (textItemAdded)="addOption('options', $event)" + (imageItemAdded)="addImageOption()" + (itemRemoved)="removeOption('options', $event)" + (itemReordered)="moveOption('options', $event)" + (itemEdited)="editOption('options', $event)"> </aspect-option-list-panel> <!--likert--> <aspect-option-list-panel *ngIf="combinedProperties.rows !== undefined" + [combinedProperties]="combinedProperties" [itemList]="$any(combinedProperties).rows | LikertRowLabel" [title]="'rows'" [textFieldLabel]="'Neue Zeile'" - (changedItemOrder)="moveLikertRow($event)" - (addItem)="addLikertRow($event)" - (removeItem)="removeLikertRow($event)" - (editItem)="editLikertRow($event)"> + (itemReordered)="moveLikertRow($event)" + (textItemAdded)="addLikertRow($event)" + (imageItemAdded)="addLikertRowImage()" + (itemRemoved)="removeLikertRow($event)" + (itemEdited)="editLikertRow($event)"> </aspect-option-list-panel> ` }) @@ -62,6 +66,20 @@ export class OptionsFieldSetComponent { }); } + addImageOption() { + const selectedElements = this.selectionService.getSelectedElements() as OptionElement[]; + const newLabel: Label = { text: '', imgSrc: null }; + this.dialogService.showLabelEditDialog(newLabel) + .subscribe((result: Label) => { + if (result) { + selectedElements.forEach(element => { + const newValue = [...this.combinedProperties.options as Label[], result]; + this.elementService.updateElementsProperty([element], 'options', newValue); + }); + } + }); + } + removeOption(property: string, optionIndex: number): void { (this.combinedProperties[property] as Label[]).splice(optionIndex, 1); this.updateModel.emit({ @@ -77,9 +95,9 @@ export class OptionsFieldSetComponent { this.updateModel.emit({ property: property, value: this.combinedProperties[property] as Label[] }); } - async editOption(property: string, optionIndex: number): Promise<void> { + editOption(property: string, optionIndex: number): void { const selectedOption = (this.combinedProperties[property] as Label[])[optionIndex]; - await this.dialogService.showLabelEditDialog(selectedOption) + this.dialogService.showLabelEditDialog(selectedOption) .subscribe((result: Label) => { if (result) { (this.combinedProperties[property] as Label[])[optionIndex] = result; @@ -106,11 +124,32 @@ export class OptionsFieldSetComponent { this.updateModel.emit({ property: 'rows', value: this.combinedProperties.rows as LikertRowElement[] }); } + addLikertRowImage(): void { + const newRow = new LikertRowElement({ + id: this.idService.getAndRegisterNewID('likert-row'), + rowLabel: { + text: '', + imgSrc: null, + imgPosition: 'above' + }, + columnCount: (this.combinedProperties.options as unknown[]).length + } as LikertRowProperties); + const columns = this.combinedProperties.options as TextImageLabel[]; + + this.dialogService.showLikertRowEditDialog(newRow, columns) + .subscribe((result: LikertRowElement) => { + if (result) { + (this.combinedProperties.rows as LikertRowElement[]).push(result); + this.updateModel.emit({ property: 'rows', value: this.combinedProperties.rows as LikertRowElement[] }); + } + }); + } + async editLikertRow(rowIndex: number): Promise<void> { const row = (this.combinedProperties.rows as LikertRowElement[])[rowIndex] as LikertRowElement; const columns = this.combinedProperties.options as TextImageLabel[]; - await this.dialogService.showLikertRowEditDialog(row, columns) + this.dialogService.showLikertRowEditDialog(row, columns) .subscribe((result: LikertRowElement) => { if (result) { if (result.id !== row.id) { diff --git a/projects/editor/src/app/components/properties-panel/option-list-panel.component.ts b/projects/editor/src/app/components/properties-panel/option-list-panel.component.ts index fa4dcafc9..96af3414e 100644 --- a/projects/editor/src/app/components/properties-panel/option-list-panel.component.ts +++ b/projects/editor/src/app/components/properties-panel/option-list-panel.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { CdkDragDrop, CdkDropList } from '@angular/cdk/drag-drop'; +import { CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop'; import { Label } from 'common/models/elements/label-interfaces'; import { TranslateModule } from '@ngx-translate/core'; import { MatInputModule } from '@angular/material/input'; @@ -9,6 +9,8 @@ import { SharedModule } from 'common/shared.module'; import { MatIconModule } from '@angular/material/icon'; import { NgForOf, NgIf } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; +import { DialogService } from 'editor/src/app/services/dialog.service'; +import { CombinedProperties } from 'editor/src/app/components/properties-panel/element-properties-panel.component'; @Component({ selector: 'aspect-option-list-panel', @@ -24,16 +26,32 @@ import { MatButtonModule } from '@angular/material/button'; SharedModule // TODO make pipe standalone and remove ], template: ` - <fieldset class="fx-column-start-stretch"> - <legend>{{title | translate }}</legend> + <fieldset *ngIf="title" class="fx-column-start-stretch"> + <legend>{{ title | translate }}</legend> + <ng-container *ngTemplateOutlet="optionList"></ng-container> + </fieldset> + + <div *ngIf="!title"> + <ng-container *ngTemplateOutlet="optionList"></ng-container> + </div> + + <ng-template #optionList> <mat-form-field appearance="outline"> - <mat-label>{{textFieldLabel}}</mat-label> + <mat-label>{{ textFieldLabel }}</mat-label> <textarea #newItem matInput cdkTextareaAutosize type="text" + (keydown.enter)="$event.stopPropagation(); $event.preventDefault();" (keyup.enter)="addListItem(newItem.value); newItem.select()"></textarea> <button mat-icon-button matSuffix color="primary" + [disabled]="newItem.value === ''" (click)="addListItem(newItem.value); newItem.select()"> <mat-icon>add</mat-icon> </button> + <button *ngIf="combinedProperties.type !== 'dropdown' && combinedProperties.type !== 'radio'" + mat-icon-button matSuffix color="primary" + [matTooltip]="'Option mit Bild hinzufügen'" + (click)="addImageOption()"> + <mat-icon>image</mat-icon> + </button> </mat-form-field> <div class="drop-list" cdkDropList [cdkDropListData]="itemList" @@ -47,7 +65,7 @@ import { MatButtonModule } from '@angular/material/button'; <img [src]="$any(item).imgSrc" [style.object-fit]="'scale-down'" [style.height.px]="40"> <button mat-icon-button color="primary" - (click)="editItem.emit(i)"> + (click)="editItem(i)"> <mat-icon>build</mat-icon> </button> <button mat-icon-button color="primary" @@ -56,7 +74,7 @@ import { MatButtonModule } from '@angular/material/button'; </button> </div> </div> - </fieldset> + </ng-template> `, styles: [` .item-box { @@ -76,23 +94,73 @@ import { MatButtonModule } from '@angular/material/button'; `] }) export class OptionListPanelComponent { - @Input() title!: string; + @Input() combinedProperties!: CombinedProperties; + @Input() title: string | undefined; // Fieldset is only rendered when given @Input() textFieldLabel!: string; @Input() itemList!: Label[]; - @Output() addItem = new EventEmitter<string>(); - @Output() removeItem = new EventEmitter<number>(); - @Output() editItem = new EventEmitter<number>(); - @Output() changedItemOrder = new EventEmitter<{ previousIndex: number, currentIndex: number }>(); + @Input() localMode: boolean = false; // Edit list here instead of emitting events for everything + @Output() textItemAdded = new EventEmitter<string>(); + @Output() imageItemAdded = new EventEmitter<void>(); + @Output() itemRemoved = new EventEmitter<number>(); + @Output() itemEdited = new EventEmitter<number>(); + @Output() itemReordered = new EventEmitter<{ previousIndex: number, currentIndex: number }>(); + @Output() itemListUpdated = new EventEmitter<void>(); + + constructor(private dialogService: DialogService) {} addListItem(text: string): void { - this.addItem.emit(text); + if (this.localMode) { + this.itemList.push({ text }); + this.itemListUpdated.emit(); + } else { + this.textItemAdded.emit(text); + } + } + + addImageOption() { + if (this.localMode) { + const newLabel: Label = { text: '', imgSrc: null }; + this.dialogService.showLabelEditDialog(newLabel) + .subscribe((result: Label) => { + if (result) { + this.itemList.push(result); + this.itemListUpdated.emit(); + } + }); + } else { + this.imageItemAdded.emit(); + } } removeListItem(itemIndex: number): void { - this.removeItem.emit(itemIndex); + if (this.localMode) { + this.itemList.splice(itemIndex, 1); + this.itemListUpdated.emit(); + } else { + this.itemRemoved.emit(itemIndex); + } + } + + editItem(itemIndex: number): void { + if (this.localMode) { + this.dialogService.showLabelEditDialog(this.itemList[itemIndex]) + .subscribe((result: Label) => { + if (result) { + this.itemList[itemIndex] = result; + this.itemListUpdated.emit(); + } + }); + } else { + this.itemEdited.emit(itemIndex); + } } moveListValue(event: CdkDragDrop<Label[]>): void { - this.changedItemOrder.emit({ previousIndex: event.previousIndex, currentIndex: event.currentIndex }); + if (this.localMode) { + moveItemInArray(this.itemList, event.previousIndex, event.currentIndex); + this.itemListUpdated.emit(); + } else { + this.itemReordered.emit({ previousIndex: event.previousIndex, currentIndex: event.currentIndex }); + } } } -- GitLab