diff --git a/projects/common/element-components/compound-elements/drop-list.component.ts b/projects/common/element-components/compound-elements/drop-list.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..ddce3b9b131e2aa6a9723de31f8a5fbcf3b1c872 --- /dev/null +++ b/projects/common/element-components/compound-elements/drop-list.component.ts @@ -0,0 +1,43 @@ +import { Component } from '@angular/core'; +import { CdkDragDrop } from '@angular/cdk/drag-drop/drag-events'; +import { moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; +import { DropListElement } from '../../models/compound-elements/drop-list'; +import { FormElementComponent } from '../../form-element-component.directive'; + +@Component({ + selector: 'app-drop-list', + template: ` + <div class="list" cdkDropList + [id]="elementModel.id" + [cdkDropListData]="this" + [cdkDropListConnectedTo]="elementModel.connectedTo" + (cdkDropListDropped)="drop($event)"> + <div class="item" *ngFor="let option of elementModel.options" cdkDrag> + {{option}} + </div> + </div> + `, + styles: [ + '.list {border: 1px solid; border-radius: 3px}', + '.item {padding: 10px;}', + '.item:not(:last-child) {border-bottom: 1px solid;}' + ] +}) +export class DropListComponent extends FormElementComponent { + elementModel!: DropListElement; + + drop(event: CdkDragDrop<DropListComponent>): void { + if (event.previousContainer === event.container) { + moveItemInArray(event.container.data.elementModel.options, event.previousIndex, event.currentIndex); + } else { + transferArrayItem( + event.previousContainer.data.elementModel.options, + event.container.data.elementModel.options, + event.previousIndex, + event.currentIndex + ); + event.previousContainer.data.elementFormControl.setValue(event.previousContainer.data.elementModel.options); + } + this.elementFormControl.setValue(event.container.data.elementModel.options); + } +} diff --git a/projects/common/id.service.ts b/projects/common/id.service.ts index 67748cf21ee1dc23c48c16ea593842cb4395deed..1ac383e16c8a501315dbffcac493df7a1a350704 100644 --- a/projects/common/id.service.ts +++ b/projects/common/id.service.ts @@ -16,7 +16,8 @@ export class IdService { video: 0, likert: 0, likert_row: 0, - 'radio-group-images': 0 + 'radio-group-images': 0, + 'drop-list': 0 }; static getInstance(): IdService { diff --git a/projects/common/models/compound-elements/drop-list.ts b/projects/common/models/compound-elements/drop-list.ts new file mode 100644 index 0000000000000000000000000000000000000000..d4d43ad531fefadcbef3acfabd49e06580a9d6b7 --- /dev/null +++ b/projects/common/models/compound-elements/drop-list.ts @@ -0,0 +1,28 @@ +import { InputElement, UIElement } from '../uI-element'; +import { FontElement, SurfaceUIElement } from '../../interfaces/UIElementInterfaces'; +import { initFontElement, initSurfaceElement } from '../../util/unit-interface-initializer'; + +export class DropListElement extends InputElement implements FontElement, SurfaceUIElement { + options: string[] = []; + connectedTo: string[] = []; + + fontColor: string = 'black'; + font: string = 'Roboto'; + fontSize: number = 18; + lineHeight: number = 120; + bold: boolean = false; + italic: boolean = false; + underline: boolean = false; + + backgroundColor: string = 'transparent'; + + constructor(serializedElement: UIElement) { + super(serializedElement); + Object.assign(this, serializedElement); + Object.assign(this, initFontElement(serializedElement)); + Object.assign(this, initSurfaceElement(serializedElement)); + + this.height = serializedElement.height || 100; + this.backgroundColor = serializedElement.backgroundColor as string || 'transparent'; + } +} diff --git a/projects/common/models/uI-element.ts b/projects/common/models/uI-element.ts index 191e11895ba3f21cd808bf17121f0a77ecc051db..7941442aa16efcf44832f327fa01114f6f77ade2 100644 --- a/projects/common/models/uI-element.ts +++ b/projects/common/models/uI-element.ts @@ -3,7 +3,7 @@ import { IdService } from '../id.service'; import { LikertColumn, LikertRow } from '../interfaces/UIElementInterfaces'; export type UIElementType = 'text' | 'button' | 'text-field' | 'text-area' | 'checkbox' -| 'dropdown' | 'radio' | 'image' | 'audio' | 'video' | 'likert' | 'likert_row' | 'radio-group-images'; +| 'dropdown' | 'radio' | 'image' | 'audio' | 'video' | 'likert' | 'likert_row' | 'radio-group-images' | 'drop-list'; export type InputElementValue = string | number | boolean | null; export interface ValueChangeElement { diff --git a/projects/common/shared.module.ts b/projects/common/shared.module.ts index 8d001cd427d713502d398a436c947a9f9b7f7cab..630a28e88f58c10814c5771287add33fb491b326 100644 --- a/projects/common/shared.module.ts +++ b/projects/common/shared.module.ts @@ -42,6 +42,7 @@ import { LikertComponent } from './element-components/compound-elements/likert.c import { LikertRadioButtonGroupComponent } from './element-components/compound-elements/likert-radio-button-group.component'; import { Magnifier } from './element-components/magnifier.component'; import { RadioGroupImagesComponent } from './element-components/compound-elements/radio-group-images.component'; +import { DropListComponent } from './element-components/compound-elements/drop-list.component'; @NgModule({ imports: [ @@ -81,7 +82,8 @@ import { RadioGroupImagesComponent } from './element-components/compound-element LikertComponent, LikertRadioButtonGroupComponent, Magnifier, - RadioGroupImagesComponent + RadioGroupImagesComponent, + DropListComponent ], exports: [ CommonModule, diff --git a/projects/common/util/element.factory.ts b/projects/common/util/element.factory.ts index 2a73ea731c9628d747cb54fb3e7b61fd4c555d5a..3cbafa0441db23d4c6b0346e7798982431227704 100644 --- a/projects/common/util/element.factory.ts +++ b/projects/common/util/element.factory.ts @@ -25,6 +25,8 @@ import { LikertElement } from '../models/compound-elements/likert-element'; import { LikertComponent } from '../element-components/compound-elements/likert.component'; import { RadioGroupImagesComponent } from '../element-components/compound-elements/radio-group-images.component'; import { RadioGroupImagesElement } from '../models/compound-elements/radio-group-images'; +import { DropListComponent } from '../element-components/compound-elements/drop-list.component'; +import { DropListElement } from '../models/compound-elements/drop-list'; export function createElement(elementModel: UIElement): UIElement { let newElement: UIElement; @@ -65,6 +67,9 @@ export function createElement(elementModel: UIElement): UIElement { case 'radio-group-images': newElement = new RadioGroupImagesElement(elementModel); break; + case 'drop-list': + newElement = new DropListElement(elementModel); + break; default: throw new Error(`ElementType ${elementModel.type} not found!`); } @@ -101,6 +106,8 @@ export function getComponentFactory( return componentFactoryResolver.resolveComponentFactory(LikertComponent); case 'radio-group-images': return componentFactoryResolver.resolveComponentFactory(RadioGroupImagesComponent); + case 'drop-list': + return componentFactoryResolver.resolveComponentFactory(DropListComponent); default: throw new Error('unknown element'); } diff --git a/projects/editor/src/app/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html b/projects/editor/src/app/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html index c5551236ec3aaef1abcda05411059e23388a5e48..1db0c946ee56cd60a2190c088b19fede5730da0a 100644 --- a/projects/editor/src/app/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html +++ b/projects/editor/src/app/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html @@ -44,6 +44,11 @@ <mat-icon>radio_button_checked</mat-icon> Optionsfelder (Bild) </button> + <button mat-raised-button (click)="addUIElement('drop-list')" + draggable="true" (dragstart)="$event.dataTransfer?.setData('elementType','drop-list')"> + <mat-icon>drag_indicator</mat-icon> + Ablegeliste + </button> <button mat-raised-button (click)="addUIElement('image')" draggable="true" (dragstart)="$event.dataTransfer?.setData('elementType','image')"> <mat-icon>image</mat-icon> diff --git a/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.html b/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.html index 6f0e38feb9583b07e3db00cc26ffd82c63a9659a..fe8ac0e023f20bb7c9260a3d8098d5cca1113596 100644 --- a/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.html +++ b/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.html @@ -84,7 +84,7 @@ {{option}} </div> <button mat-icon-button color="primary" - (click)="editTextOption(i)"> + (click)="editTextOption('options', i)"> <mat-icon>build</mat-icon> </button> <button mat-icon-button color="primary" @@ -148,7 +148,7 @@ [width]="200"> </ng-container> - <mat-form-field *ngIf="combinedProperties.options !== undefined" + <mat-form-field *ngIf="combinedProperties.options !== undefined && !combinedProperties.connectedTo" appearance="fill"> <mat-label>{{'propertiesPanel.preset' | translate }}</mat-label> <mat-select [value]="combinedProperties.value" @@ -374,6 +374,37 @@ {{combinedProperties.magnifierZoom}} </div> + <mat-form-field disabled="true" *ngIf="combinedProperties.connectedTo !== undefined"> + <ng-container> + <mat-label>{{'propertiesPanel.connectedDropList' | translate }}</mat-label> + <div class="drop-list" cdkDropList [cdkDropListData]="combinedProperties.connectedTo" + (cdkDropListDropped)="reorderOptions('connectedTo', $any($event))"> + <div *ngFor="let connectedTo of $any(combinedProperties.connectedTo); let i = index" cdkDrag + class="list-items" fxLayout="row" fxLayoutAlign="end center"> + <div fxFlex="70"> + {{connectedTo}} + </div> + <button mat-icon-button color="primary" + (click)="editTextOption('connectedTo', i)"> + <mat-icon>build</mat-icon> + </button> + <button mat-icon-button color="primary" + (click)="removeOption('connectedTo', connectedTo)"> + <mat-icon>clear</mat-icon> + </button> + </div> + </div> + </ng-container> + <div fxLayout="row" fxLayoutAlign="center center"> + <button mat-icon-button matPrefix + (click)="addOption('connectedTo', newconnectedTo.value); newconnectedTo.select()"> + <mat-icon>add</mat-icon> + </button> + <input #newconnectedTo matInput type="text" + (keyup.enter)="addOption('connectedTo', newconnectedTo.value); newconnectedTo.select()"> + </div> + </mat-form-field> + <mat-divider></mat-divider> <button mat-raised-button class="element-button" diff --git a/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.ts b/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.ts index 044e66613f446455097cc90e435fb23a627972d8..2feab2233bd8615ce3cdb8038b6d7cea21985ec0 100644 --- a/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.ts +++ b/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.ts @@ -145,8 +145,8 @@ export class ElementPropertiesComponent implements OnInit, OnDestroy { this.updateModel('rows', this.combinedProperties.rows as LikertElementRow[]); } - async editTextOption(optionIndex: number): Promise<void> { - await this.unitService.editTextOption(optionIndex); + async editTextOption(property: string, optionIndex: number): Promise<void> { + await this.unitService.editTextOption(property, optionIndex); } async editColumnOption(optionIndex: number): Promise<void> { diff --git a/projects/editor/src/app/unit.service.ts b/projects/editor/src/app/unit.service.ts index 80f8fca50dfbb2819d802e488ac669d3446fb1e9..e0b95f66be1fcb4f1b5466c3ab6f764b898687e8 100644 --- a/projects/editor/src/app/unit.service.ts +++ b/projects/editor/src/app/unit.service.ts @@ -220,8 +220,8 @@ export class UnitService { return true; } - async editTextOption(optionIndex: number): Promise<void> { - const oldOptions = this.selectionService.getSelectedElements()[0].options as string[]; + async editTextOption(property: string, optionIndex: number): Promise<void> { + const oldOptions = this.selectionService.getSelectedElements()[0][property] as string[]; await this.dialogService.showTextEditDialog(oldOptions[optionIndex]) .subscribe((result: string) => { if (result) { diff --git a/projects/editor/src/assets/i18n/de.json b/projects/editor/src/assets/i18n/de.json index 1ea5afe76df46810398b61bdc7c04170857fec40..3c639e0d54157c800ddab45d4dba754ac93e632d 100644 --- a/projects/editor/src/assets/i18n/de.json +++ b/projects/editor/src/assets/i18n/de.json @@ -103,7 +103,8 @@ "lineColoringColor": "Zeilenfarbe", "magnifier": "Lupe", "magnifierSize": "Größe der Lupe", - "magnifierZoom": "Vergrößerung der Lupe", + "magnifierZoom": "Vergrößerungsfaktor", + "connectedDropList": "Verbundene Ablegelisten", "duplicateElement": "Element duplizieren", "deleteElement": "Element löschen", "noElementSelected": "Kein Element ausgewählt"