From bda12d70dfecf6f2b419360f2f438bb05ef6de0d Mon Sep 17 00:00:00 2001 From: rhenck <richard.henck@iqb.hu-berlin.de> Date: Tue, 30 Nov 2021 11:26:56 +0100 Subject: [PATCH] Refactor dnd values to be objects with IDs and add image support - Dnd values now have a special type that has an ID, a string and another string for an image path. - A new dialog exists for editing thoe options. Similarly to the likert header. --- projects/common/models/uI-element.ts | 9 +++- .../drop-list/drop-list.component.ts | 29 +++++++----- projects/editor/src/app/app.module.ts | 4 +- .../drop-list-option-edit-dialog.component.ts | 44 +++++++++++++++++++ .../element-model-properties.component.html | 11 +++-- .../element-model-properties.component.ts | 11 +++++ .../element-properties.component.ts | 10 ++++- .../editor/src/app/services/dialog.service.ts | 17 ++++++- .../editor/src/app/services/unit.service.ts | 19 +++++++- 9 files changed, 131 insertions(+), 23 deletions(-) create mode 100644 projects/editor/src/app/components/dialogs/drop-list-option-edit-dialog.component.ts diff --git a/projects/common/models/uI-element.ts b/projects/common/models/uI-element.ts index 5c64b37e0..19042331a 100644 --- a/projects/common/models/uI-element.ts +++ b/projects/common/models/uI-element.ts @@ -4,7 +4,12 @@ import { IdService } from '../id.service'; export type UIElementType = 'text' | 'button' | 'text-field' | 'text-area' | 'checkbox' | 'dropdown' | 'radio' | 'image' | 'audio' | 'video' | 'likert' | 'likert_row' | 'radio-group-images' | 'drop-list' | 'cloze'; -export type InputElementValue = string[] | string | number | boolean | null; +export type InputElementValue = string[] | string | number | boolean | DragNDropValueObject | null; +export type DragNDropValueObject = { + id: string; + stringValue?: string; + imgSrcValue?: string; +}; export abstract class UIElement { [index: string]: any; @@ -45,7 +50,7 @@ export abstract class UIElement { // This can be overwritten by elements if they need to handle some property specifics. Likert does. setProperty(property: string, - value: InputElementValue | string[] | LikertColumn[] | LikertRow[]): void { + value: InputElementValue | string[] | LikertColumn[] | LikertRow[] | DragNDropValueObject[]): void { if (this.fontProps && property in this.fontProps) { this.fontProps[property] = value as string | number | boolean; } else if (this.surfaceProps && property in this.surfaceProps) { diff --git a/projects/common/ui-elements/drop-list/drop-list.component.ts b/projects/common/ui-elements/drop-list/drop-list.component.ts index 9fe6c7fb9..dc37aa5dd 100644 --- a/projects/common/ui-elements/drop-list/drop-list.component.ts +++ b/projects/common/ui-elements/drop-list/drop-list.component.ts @@ -32,18 +32,25 @@ import { FormElementComponent } from '../../directives/form-element-component.di [cdkDropListOrientation]="elementModel.orientation" [cdkDropListEnterPredicate]="onlyOneItemPredicate" (cdkDropListDropped)="drop($event)"> - <div class="item" *ngFor="let value of $any(elementModel.value)" cdkDrag - [style.background-color]="elementModel.itemBackgroundColor" - (cdkDragStarted)=dragStart() (cdkDragEnded)="dragEnd()"> - <div *cdkDragPreview - [style.font-size.px]="elementModel.fontProps.fontSize" - [style.background-color]="elementModel.itemBackgroundColor"> - {{value}} + <ng-container *ngFor="let value of $any(elementModel.value)"> + <div class="item" *ngIf="!value.imgSrcValue" cdkDrag + [style.background-color]="elementModel.itemBackgroundColor" + (cdkDragStarted)=dragStart() (cdkDragEnded)="dragEnd()"> + <div *cdkDragPreview + [style.font-size.px]="elementModel.fontProps.fontSize" + [style.background-color]="elementModel.itemBackgroundColor"> + {{value.stringValue}} + </div> + <div class="drag-placeholder" *cdkDragPlaceholder [style.min-height.px]="elementModel.fontProps.fontSize"> + </div> + {{value.stringValue}} </div> - <div class="drag-placeholder" *cdkDragPlaceholder [style.min-height.px]="elementModel.fontProps.fontSize"> - </div> - {{value}} - </div> + <img *ngIf="value.imgSrcValue" + [src]="value.imgSrcValue | safeResourceUrl" alt="Image Placeholder" + + cdkDrag (cdkDragStarted)=dragStart() (cdkDragEnded)="dragEnd()" + [style.object-fit]="'scale-down'"> + </ng-container> </div> <mat-error *ngIf="elementFormControl.errors && elementFormControl.touched" class="error-message"> diff --git a/projects/editor/src/app/app.module.ts b/projects/editor/src/app/app.module.ts index 52be52c2e..7ea87272d 100644 --- a/projects/editor/src/app/app.module.ts +++ b/projects/editor/src/app/app.module.ts @@ -43,6 +43,7 @@ import { LikertColumnEditDialogComponent } from './components/dialogs/likert-col import { LikertRowEditDialogComponent } from './components/dialogs/likert-row-edit-dialog.component'; import { RichTextEditDialogComponent } from './components/dialogs/rich-text-edit-dialog.component'; import { ElementModelPropertiesComponent } from './components/unit-view/page-view/properties-panel/element-model-properties.component'; +import { DropListOptionEditDialogComponent } from './components/dialogs/drop-list-option-edit-dialog.component'; @NgModule({ declarations: [ @@ -68,7 +69,8 @@ import { ElementModelPropertiesComponent } from './components/unit-view/page-vie LikertColumnEditDialogComponent, LikertRowEditDialogComponent, RichTextEditDialogComponent, - ElementModelPropertiesComponent + ElementModelPropertiesComponent, + DropListOptionEditDialogComponent ], imports: [ BrowserModule, diff --git a/projects/editor/src/app/components/dialogs/drop-list-option-edit-dialog.component.ts b/projects/editor/src/app/components/dialogs/drop-list-option-edit-dialog.component.ts new file mode 100644 index 000000000..57f9aa9e0 --- /dev/null +++ b/projects/editor/src/app/components/dialogs/drop-list-option-edit-dialog.component.ts @@ -0,0 +1,44 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { DragNDropValueObject } from '../../../../../common/models/uI-element'; +import { FileService } from '../../../../../common/file.service'; + +@Component({ + selector: 'app-drop-list-option-edit-dialog', + template: ` + <mat-dialog-content fxLayout="column"> + <mat-form-field> + <mat-label>{{'text' | translate }}</mat-label> + <input #textField matInput type="text" [value]="data.value.stringValue"> + </mat-form-field> + <input #imageUpload type="file" hidden (click)="loadImage()"> + <button mat-raised-button (click)="imageUpload.click()">{{ 'loadImage' | translate }}</button> + <button mat-raised-button (click)="imgSrc = undefined">{{ 'removeImage' | translate }}</button> + <img [src]="imgSrc" + [style.object-fit]="'scale-down'" + [width]="200"> + <mat-form-field> + <mat-label>{{'id' | translate }}</mat-label> + <input #idField matInput type="text" [value]="data.value.id"> + </mat-form-field> + </mat-dialog-content> + <mat-dialog-actions> + <button mat-button [mat-dialog-close]="{ + stringValue: textField.value, + imgSrcValue: imgSrc, + id: idField.value + } "> + {{'save' | translate }} + </button> + <button mat-button mat-dialog-close>{{'cancel' | translate }}</button> + </mat-dialog-actions> + ` +}) +export class DropListOptionEditDialogComponent { + constructor(@Inject(MAT_DIALOG_DATA) public data: { value: DragNDropValueObject }) { } + imgSrc: string | undefined = this.data.value.imgSrcValue; + + async loadImage(): Promise<void> { + this.imgSrc = await FileService.loadImage(); + } +} diff --git a/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-model-properties.component.html b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-model-properties.component.html index 1037e5d9a..db17c834b 100644 --- a/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-model-properties.component.html +++ b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-model-properties.component.html @@ -110,10 +110,13 @@ <div *ngFor="let value of $any(combinedProperties.value); let i = index" cdkDrag class="list-items" fxLayout="row" fxLayoutAlign="end center"> <div fxFlex="70"> - {{value}} + {{value.stringValue}} ({{value.id}}) </div> + <img [src]="value.imgSrcValue" + [style.object-fit]="'scale-down'" + [style.height.px]="40"> <button mat-icon-button color="primary" - (click)="editTextOption('value', i)"> + (click)="editDropListOption(i)"> <mat-icon>build</mat-icon> </button> <button mat-icon-button color="primary" @@ -125,11 +128,11 @@ </ng-container> <div fxLayout="row" fxLayoutAlign="center center"> <button mat-icon-button matPrefix - (click)="addOption('value', newValue.value); newValue.select()"> + (click)="addDropListOption(newValue.value); newValue.select()"> <mat-icon>add</mat-icon> </button> <input #newValue matInput type="text" - (keyup.enter)="addOption('value', newValue.value); newValue.select()"> + (keyup.enter)="addDropListOption(newValue.value); newValue.select()"> </div> </mat-form-field> diff --git a/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-model-properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-model-properties.component.ts index 43d698e05..88faf36cd 100644 --- a/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-model-properties.component.ts +++ b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-model-properties.component.ts @@ -5,6 +5,7 @@ import { import { CdkDragDrop } from '@angular/cdk/drag-drop/drag-events'; import { moveItemInArray } from '@angular/cdk/drag-drop'; import { + DragNDropValueObject, InputElementValue, LikertColumn, LikertRow, UIElement } from '../../../../../../../common/models/uI-element'; import { LikertElement } from '../../../../../../../common/ui-elements/likert/likert-element'; @@ -50,6 +51,16 @@ export class ElementModelPropertiesComponent { await this.unitService.editTextOption(property, optionIndex); } + addDropListOption(value: string): void { + const id = this.unitService.getNewValueID(); + this.combinedProperties.value.push({ stringValue: value, id: id }); + this.updateModel.emit({ property: 'value', value: this.combinedProperties.value }); + } + + async editDropListOption(optionIndex: number): Promise<void> { + await this.unitService.editDropListOption(optionIndex); + } + async editColumnOption(optionIndex: number): Promise<void> { await this.unitService.editLikertColumn(this.selectedElements as LikertElement[], optionIndex); } diff --git a/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-properties.component.ts index 41bcc0e6a..cf5469d8b 100644 --- a/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-properties.component.ts +++ b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-properties.component.ts @@ -8,7 +8,12 @@ import { TranslateService } from '@ngx-translate/core'; import { UnitService } from '../../../../services/unit.service'; import { SelectionService } from '../../../../services/selection.service'; import { MessageService } from '../../../../../../../common/services/message.service'; -import { LikertColumn, LikertRow, UIElement } from '../../../../../../../common/models/uI-element'; +import { + DragNDropValueObject, + LikertColumn, + LikertRow, + UIElement +} from '../../../../../../../common/models/uI-element'; @Component({ selector: 'app-element-properties', @@ -73,7 +78,8 @@ export class ElementPropertiesComponent implements OnInit, OnDestroy { } updateModel(property: string, - value: string | number | boolean | string[] | LikertColumn[] | LikertRow[] | null, + value: string | number | boolean | string[] | + LikertColumn[] | LikertRow[] | DragNDropValueObject | null, isInputValid: boolean | null = true): void { if (isInputValid) { this.unitService.updateElementProperty(this.selectedElements, property, value); diff --git a/projects/editor/src/app/services/dialog.service.ts b/projects/editor/src/app/services/dialog.service.ts index 06b248aaf..36f46befe 100644 --- a/projects/editor/src/app/services/dialog.service.ts +++ b/projects/editor/src/app/services/dialog.service.ts @@ -9,7 +9,13 @@ import { RichTextEditDialogComponent } from '../components/dialogs/rich-text-edi import { PlayerEditDialogComponent } from '../components/dialogs/player-edit-dialog.component'; import { LikertColumnEditDialogComponent } from '../components/dialogs/likert-column-edit-dialog.component'; import { LikertRowEditDialogComponent } from '../components/dialogs/likert-row-edit-dialog.component'; -import { LikertColumn, PlayerElement, PlayerProperties } from '../../../../common/models/uI-element'; +import { + DragNDropValueObject, + LikertColumn, + PlayerElement, + PlayerProperties +} from '../../../../common/models/uI-element'; +import { DropListOptionEditDialogComponent } from '../components/dialogs/drop-list-option-edit-dialog.component'; @Injectable({ providedIn: 'root' @@ -35,6 +41,15 @@ export class DialogService { return dialogRef.afterClosed(); } + showDropListOptionEditDialog(value: DragNDropValueObject): Observable<DragNDropValueObject> { + const dialogRef = this.dialog.open(DropListOptionEditDialogComponent, { + data: { + value: value + } + }); + return dialogRef.afterClosed(); + } + showMultilineTextEditDialog(text: string): Observable<string> { const dialogRef = this.dialog.open(TextEditMultilineDialogComponent, { data: { diff --git a/projects/editor/src/app/services/unit.service.ts b/projects/editor/src/app/services/unit.service.ts index a9325248c..a2dcff6d3 100644 --- a/projects/editor/src/app/services/unit.service.ts +++ b/projects/editor/src/app/services/unit.service.ts @@ -209,8 +209,8 @@ export class UnitService { } updateElementProperty(elements: UIElement[], property: string, - value: string | number | boolean | string[] | - LikertColumn[] | LikertRow[] | null): boolean { + value: InputElementValue | LikertColumn[] | LikertRow[] | + DragNDropValueObject[] | null): boolean { console.log('updateElementProperty', elements, property, value); for (const element of elements) { if (property === 'id') { @@ -239,6 +239,21 @@ export class UnitService { }); } + async editDropListOption(optionIndex: number): Promise<void> { + const oldOptions = this.selectionService.getSelectedElements()[0].value as DragNDropValueObject[]; + await this.dialogService.showDropListOptionEditDialog(oldOptions[optionIndex]) + .subscribe((result: DragNDropValueObject) => { + if (result) { + if (result.id !== oldOptions[optionIndex].id && !IdService.getInstance().isIdAvailable(result.id)) { + this.messageService.showError(this.translateService.instant('idTaken')); + return; + } + oldOptions[optionIndex] = result; + this.updateElementProperty(this.selectionService.getSelectedElements(), 'value', oldOptions); + } + }); + } + async editLikertRow(row: LikertElementRow, columns: LikertColumn[]): Promise<void> { await this.dialogService.showLikertRowEditDialog(row, columns) .subscribe((result: LikertElementRow) => { -- GitLab