diff --git a/projects/common/component-utils.ts b/projects/common/component-utils.ts index 7ac8d280f027e8ecf40d96dbd74cbb67a2896791..2fb91550f6f5fb5cb6cde5fa8ee5f1257482a0ec 100644 --- a/projects/common/component-utils.ts +++ b/projects/common/component-utils.ts @@ -9,6 +9,7 @@ import { RadioButtonGroupComponent } from './element-components/radio-button-gro import { ImageComponent } from './element-components/image.component'; import { AudioComponent } from './element-components/audio.component'; import { VideoComponent } from './element-components/video.component'; +import { LikertComponent } from './element-components/compound-elements/likert.component'; export function getComponentFactory( elementType: string, componentFactoryResolver: ComponentFactoryResolver @@ -35,6 +36,8 @@ export function getComponentFactory( return componentFactoryResolver.resolveComponentFactory(AudioComponent); case 'video': return componentFactoryResolver.resolveComponentFactory(VideoComponent); + case 'likert': + return componentFactoryResolver.resolveComponentFactory(LikertComponent); default: throw new Error('unknown element'); } diff --git a/projects/common/element-components/compound-elements/likert-radio-button-group.component.ts b/projects/common/element-components/compound-elements/likert-radio-button-group.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..c27afa8259d30c714e830db9f927b298f5b87041 --- /dev/null +++ b/projects/common/element-components/compound-elements/likert-radio-button-group.component.ts @@ -0,0 +1,33 @@ +import { Component, Input } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { FormElementComponent } from '../../form-element-component.directive'; +import { LikertElementRow } from '../../models/complex-elements/likert-element-row'; + +@Component({ + selector: 'app-likert-radio-button-group', + template: ` + <mat-radio-group [style.display]="'grid'" + [style.grid-template-columns]="'2fr ' + '1fr '.repeat(elementModel.columnCount)" + [formControl]="elementFormControl"> + <div [style.grid-column-start]="1" + [style.grid-column-end]="2" + [style.grid-row-start]="1" + [style.grid-row-end]="2"> + {{elementModel.text}} + {{elementModel.id}} + </div> + + <mat-radio-button *ngFor="let answer of [].constructor(elementModel.columnCount); let j = index" + [value]="j" + [style.grid-column-start]="2 + j" + [style.grid-column-end]="3 + j" + [style.grid-row-start]="1" + [style.grid-row-end]="2"> + </mat-radio-button> + </mat-radio-group> + ` +}) +export class LikertRadioButtonGroupComponent extends FormElementComponent { + @Input() elementModel!: LikertElementRow; + @Input() parentForm!: FormGroup; +} diff --git a/projects/common/element-components/compound-elements/likert.component.ts b/projects/common/element-components/compound-elements/likert.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..0c5aead967d6425e7807f8a2ee1cc01a4a85c7b0 --- /dev/null +++ b/projects/common/element-components/compound-elements/likert.component.ts @@ -0,0 +1,53 @@ +import { Component } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { LikertElement } from '../../models/complex-elements/likert-element'; + +@Component({ + selector: 'app-likert', + template: ` + <div [style.display]="'grid'" + [style.grid-template-columns]="'2fr ' + '1fr '.repeat(elementModel.answers.length)"> + + <div [style.display]="'grid'" + [style.grid-template-columns]="'2fr ' + '1fr '.repeat(elementModel.answers.length)" + [style.grid-column-start]="1" + [style.grid-column-end]="elementModel.answers.length + 2" + [style.grid-row-start]="1" + [style.grid-row-end]="2"> + <div *ngFor="let answer of elementModel.answers; let i = index" class="answers" + [style.grid-column-start]="2 + i" + [style.grid-column-end]="3 + i" + [style.grid-row-start]="1" + [style.grid-row-end]="2"> + <img *ngIf="answer.imgSrc && answer.position === 'above'" [src]="answer.imgSrc" alt="Image Placeholder" + [style.object-fit]="'scale-down'" + [width]="200"> + {{answer.text}} + <img *ngIf="answer.imgSrc && answer.position === 'below'" [src]="answer.imgSrc" alt="Image Placeholder" + [style.object-fit]="'scale-down'" + [width]="200"> + </div> + </div> + + <ng-container *ngFor="let question of elementModel.questions; let i = index"> + <app-likert-radio-button-group [style.display]="'grid'" + [style.grid-column-start]="1" + [style.grid-column-end]="elementModel.answers.length + 2" + [style.grid-row-start]="2 + i" + [style.grid-row-end]="3 + i" + [elementModel]="elementModel.questions[i]" + [parentForm]="parentForm"> + </app-likert-radio-button-group> + </ng-container> + </div> + `, + styles: [ + '.odd {background-color: #D0F6E7;}', + '.answers {text-align: center;}', + '::ng-deep app-likert mat-radio-button span.mat-radio-container {left: calc(50% - 10px)}' + ] +}) +export class LikertComponent { + elementModel!: LikertElement; + parentForm!: FormGroup; +} diff --git a/projects/common/models/compound-elements/likert-element-row.ts b/projects/common/models/compound-elements/likert-element-row.ts new file mode 100644 index 0000000000000000000000000000000000000000..70e6323daf81ca0e0727c675cbcfd06624551f0f --- /dev/null +++ b/projects/common/models/compound-elements/likert-element-row.ts @@ -0,0 +1,11 @@ +import { InputElement, UIElement } from '../uI-element'; + +export class LikertElementRow extends InputElement { + text: string = ''; + columnCount: number = 0; + + constructor(serializedElement: UIElement) { + super(serializedElement); + Object.assign(this, serializedElement); + } +} diff --git a/projects/common/models/compound-elements/likert-element.ts b/projects/common/models/compound-elements/likert-element.ts new file mode 100644 index 0000000000000000000000000000000000000000..29be35a46bded060c5bc7a02aef627d5a6cf4c18 --- /dev/null +++ b/projects/common/models/compound-elements/likert-element.ts @@ -0,0 +1,37 @@ +import { AnswerOption, UIElement } from '../uI-element'; +import { LikertElementRow } from './likert-element-row'; + +export class LikertElement extends UIElement { + questions: LikertElementRow[] = [ + new LikertElementRow({ type: 'likert_row', text: 'Testfrage', columnCount: 2 } as LikertElementRow) + ]; + + answers: AnswerOption[] = [{ + text: 'Option A', + imgSrc: null, + position: 'above' + }, { + text: 'Option B', + imgSrc: null, + position: 'above' + }]; + + lineColoring: boolean = true; + + constructor(serializedElement: UIElement, coordinates?: { x: number; y: number }) { + super(serializedElement, coordinates); + Object.assign(this, serializedElement); + + this.height = serializedElement.height || 200; + this.width = serializedElement.width || 400; + } + + setProperty(property: string, value: string | number | boolean | string[] | AnswerOption[] | null): void { + super.setProperty(property, value); + if (property === 'answers') { + this.questions.forEach(question => { + question.columnCount = this.answers.length; + }); + } + } +} diff --git a/projects/common/shared.module.ts b/projects/common/shared.module.ts index 2bc500e13ae77812cd2cca5acc7a712fcd411d34..a29ccc5bd5a5ff54493e33e2dc2e087635d2f65b 100644 --- a/projects/common/shared.module.ts +++ b/projects/common/shared.module.ts @@ -35,6 +35,8 @@ import { SafeResourceUrlPipe } from './element-components/pipes/safe-resource-ur import { InputBackgroundColorDirective } from './element-components/directives/input-background-color.directive'; import { ErrorTransformPipe } from './element-components/pipes/error-transform.pipe'; import { SafeResourceHTMLPipe } from './element-components/pipes/safe-resource-html.pipe'; +import { LikertComponent } from './element-components/compound-elements/likert.component'; +import { LikertRadioButtonGroupComponent } from './element-components/compound-elements/likert-radio-button-group.component'; @NgModule({ imports: [ @@ -67,7 +69,9 @@ import { SafeResourceHTMLPipe } from './element-components/pipes/safe-resource-h SafeResourceUrlPipe, InputBackgroundColorDirective, ErrorTransformPipe, - SafeResourceHTMLPipe + SafeResourceHTMLPipe, + LikertComponent, + LikertRadioButtonGroupComponent ], exports: [ CommonModule, diff --git a/projects/common/util/element.factory.ts b/projects/common/util/element.factory.ts index cdc50369f5caa18399183052108adde4e6d2a82b..ff0f32c1ffb501cdad0546ec553ac79a3e4a27d8 100644 --- a/projects/common/util/element.factory.ts +++ b/projects/common/util/element.factory.ts @@ -10,6 +10,7 @@ import { ImageElement } from '../models/image-element'; import { AudioElement } from '../models/audio-element'; import { VideoElement } from '../models/video-element'; import { FileService } from '../file.service'; +import { LikertElement } from '../models/compound-elements/likert-element'; export async function createElement(elementModel: UIElement, coordinates?: { x: number; y: number }): Promise<UIElement> { let newElement: UIElement; @@ -53,6 +54,9 @@ export async function createElement(elementModel: UIElement, coordinates?: { x: } newElement = new VideoElement(elementModel, coordinates); break; + case 'likert': + newElement = new LikertElement(elementModel, coordinates); + break; default: throw new Error(`ElementType ${elementModel.type} not found!`); } 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 6be31f925bf31c2a188cdcebceb1afe3440d15d3..b0ffe771d7a27fce513fbe60f59435279be26f39 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 @@ -60,6 +60,10 @@ <ng-template mat-tab-label> <mat-icon class="example-tab-icon">widgets</mat-icon> </ng-template> - Hier entstehen kombinierte Elementgruppen + <button mat-raised-button (click)="addUIElement('likert')" + draggable="true" (dragstart)="$event.dataTransfer?.setData('elementType','likert')"> + <mat-icon>toc</mat-icon> + Likert + </button> </mat-tab> </mat-tab-group>