diff --git a/package.json b/package.json index 82b9a6d59413c89ea920d27f042e19771e1a068c..8f9e2000f4f4ceb8e869748b1a8743d855d10187 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "config": { "player_version": "1.20.1", "editor_version": "1.26.1", - "unit_definition_version": "1.2.0" + "unit_definition_version": "2.0.0" }, "scripts": { "ng": "ng", diff --git a/projects/common/components/ui-elements/audio.component.ts b/projects/common/components/ui-elements/audio.component.ts index c39789d5a185217240aeb39296fd4a9505e010c0..77db4b009362f199d98566551f87690ab90fa9f0 100644 --- a/projects/common/components/ui-elements/audio.component.ts +++ b/projects/common/components/ui-elements/audio.component.ts @@ -17,7 +17,7 @@ import { AudioElement } from '../../interfaces/elements'; [project]="project" [id]="elementModel.id" [savedPlaybackTime]="savedPlaybackTime" - [playerProperties]="elementModel.playerProps" + [playerProperties]="elementModel.player" [active]="active" [dependencyDissolved]="dependencyDissolved" (onMediaValidStatusChanged)="onMediaValidStatusChanged.emit($event)" diff --git a/projects/common/components/ui-elements/button.component.ts b/projects/common/components/ui-elements/button.component.ts index 6102e11f34226b0cee7ee901db219f6d5b0d9d49..e4d8bf31abda43d4449604be11e8783aded77057 100644 --- a/projects/common/components/ui-elements/button.component.ts +++ b/projects/common/components/ui-elements/button.component.ts @@ -11,14 +11,14 @@ import { ButtonElement } from '../../interfaces/elements'; type='button' [style.width.%]="100" [style.height.%]="100" - [style.background-color]="elementModel.styles.backgroundColor" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''" - [style.border-radius.px]="elementModel.styles.borderRadius" + [style.background-color]="elementModel.styling.backgroundColor" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''" + [style.border-radius.px]="elementModel.styling.borderRadius" (click)="elementModel.action && elementModel.actionParam !== null ? navigateTo.emit({ action: elementModel.action, @@ -30,8 +30,8 @@ import { ButtonElement } from '../../interfaces/elements'; <input *ngIf="elementModel.imageSrc" type="image" [src]="elementModel.imageSrc | safeResourceUrl" - [class]="elementModel.positionProps.dynamicPositioning && - !elementModel.positionProps.fixedSize ? 'dynamic-image' : 'static-image'" + [class]="elementModel.position.dynamicPositioning && + !elementModel.position.fixedSize ? 'dynamic-image' : 'static-image'" [alt]="'imageNotFound' | translate" (click)="elementModel.action && elementModel.actionParam !== null? navigateTo.emit({ diff --git a/projects/common/components/ui-elements/checkbox.component.ts b/projects/common/components/ui-elements/checkbox.component.ts index 791bb2faa6c87871497cc7f9f8248dc341e61576..f67cdb20cb394d1db608cbc790d442f8804d34cb 100644 --- a/projects/common/components/ui-elements/checkbox.component.ts +++ b/projects/common/components/ui-elements/checkbox.component.ts @@ -8,16 +8,16 @@ import { CheckboxElement } from '../../interfaces/elements'; <div class="mat-form-field" [style.width.%]="100" [style.height.%]="100" - [style.background-color]="elementModel.styles.backgroundColor"> + [style.background-color]="elementModel.styling.backgroundColor"> <mat-checkbox #checkbox class="example-margin" [formControl]="elementFormControl" [checked]="$any(elementModel.value)" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''" (click)="elementModel.readOnly ? $event.preventDefault() : null"> <div [innerHTML]="elementModel.label"></div> </mat-checkbox> diff --git a/projects/common/components/ui-elements/cloze.component.ts b/projects/common/components/ui-elements/cloze.component.ts index 6133947e0695e3bab6e46d96b2663a83895ba59e..e1c0101dca30d116ce1596e5f1488c15dabd8170 100644 --- a/projects/common/components/ui-elements/cloze.component.ts +++ b/projects/common/components/ui-elements/cloze.component.ts @@ -48,93 +48,93 @@ import { ClozeUtils } from '../../util/cloze'; <ng-template #paragraphs let-part> <p *ngIf="part.type === 'paragraph'" - [style.line-height.%]="elementModel.styles.lineHeight" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''" + [style.line-height.%]="elementModel.styling.lineHeight" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''" [style.margin-bottom]="part.attrs?.margin + 'px'" [style.margin-left]="part.attrs?.hangingIndent ? '' : - ($any(part.attrs?.indentSize) * $any(part.attrs?.indent)) + 'px'" + ($any(part.attrs?.indentSize) * $any(part.attrs?.indent)) + 'px'" [style.text-align]="part.attrs?.textAlign" [style.text-indent]="part.attrs?.hangingIndent ? - ($any(part.attrs?.indentSize) * $any(part.attrs?.indent)) + 'px' : ''"> - <ng-container [ngTemplateOutlet]="paragraphChildren" - [ngTemplateOutletContext]="{ $implicit: part }"></ng-container> + ($any(part.attrs?.indentSize) * $any(part.attrs?.indent)) + 'px' : ''"> + <ng-container [ngTemplateOutlet]="paragraphChildren" + [ngTemplateOutletContext]="{ $implicit: part }"></ng-container> </p> <h1 *ngIf="part.type === 'heading' && part.attrs.level === 1" [style.display]="'inline'" - [style.line-height.%]="elementModel.styles.lineHeight" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''"> - <ng-container [ngTemplateOutlet]="paragraphChildren" - [ngTemplateOutletContext]="{ $implicit: part }"></ng-container> + [style.line-height.%]="elementModel.styling.lineHeight" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"> + <ng-container [ngTemplateOutlet]="paragraphChildren" + [ngTemplateOutletContext]="{ $implicit: part }"></ng-container> </h1> <h2 *ngIf="part.type === 'heading' && part.attrs.level === 2" [style.display]="'inline'" - [style.line-height.%]="elementModel.styles.lineHeight" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''"> - <ng-container [ngTemplateOutlet]="paragraphChildren" - [ngTemplateOutletContext]="{ $implicit: part }"></ng-container> + [style.line-height.%]="elementModel.styling.lineHeight" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"> + <ng-container [ngTemplateOutlet]="paragraphChildren" + [ngTemplateOutletContext]="{ $implicit: part }"></ng-container> </h2> <h3 *ngIf="part.type === 'heading' && part.attrs.level === 3" [style.display]="'inline'" - [style.line-height.%]="elementModel.styles.lineHeight" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''"> - <ng-container [ngTemplateOutlet]="paragraphChildren" - [ngTemplateOutletContext]="{ $implicit: part }"></ng-container> + [style.line-height.%]="elementModel.styling.lineHeight" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"> + <ng-container [ngTemplateOutlet]="paragraphChildren" + [ngTemplateOutletContext]="{ $implicit: part }"></ng-container> </h3> <h4 *ngIf="part.type === 'heading' && part.attrs.level === 4" [style.display]="'inline'" - [style.line-height.%]="elementModel.styles.lineHeight" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''"> - <ng-container [ngTemplateOutlet]="paragraphChildren" - [ngTemplateOutletContext]="{ $implicit: part }"></ng-container> + [style.line-height.%]="elementModel.styling.lineHeight" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"> + <ng-container [ngTemplateOutlet]="paragraphChildren" + [ngTemplateOutletContext]="{ $implicit: part }"></ng-container> </h4> <h5 *ngIf="part.type === 'heading' && part.attrs.level === 5" [style.display]="'inline'" - [style.line-height.%]="elementModel.styles.lineHeight" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''"> - <ng-container [ngTemplateOutlet]="paragraphChildren" - [ngTemplateOutletContext]="{ $implicit: part }"></ng-container> + [style.line-height.%]="elementModel.styling.lineHeight" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"> + <ng-container [ngTemplateOutlet]="paragraphChildren" + [ngTemplateOutletContext]="{ $implicit: part }"></ng-container> </h5> <h6 *ngIf="part.type === 'heading' && part.attrs.level === 6" [style.display]="'inline'" - [style.line-height.%]="elementModel.styles.lineHeight" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''"> - <ng-container [ngTemplateOutlet]="paragraphChildren" - [ngTemplateOutletContext]="{ $implicit: part }"></ng-container> + [style.line-height.%]="elementModel.styling.lineHeight" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"> + <ng-container [ngTemplateOutlet]="paragraphChildren" + [ngTemplateOutletContext]="{ $implicit: part }"></ng-container> </h6> </ng-template> diff --git a/projects/common/components/ui-elements/drop-list-simple.component.ts b/projects/common/components/ui-elements/drop-list-simple.component.ts index fb84d43babfe37d0ffbc2d168ece7f93ce779e1c..195351c1fb9f414bed1e7b374afa173041691a68 100644 --- a/projects/common/components/ui-elements/drop-list-simple.component.ts +++ b/projects/common/components/ui-elements/drop-list-simple.component.ts @@ -10,21 +10,21 @@ import { DragNDropValueObject, DropListSimpleElement } from '../../interfaces/el selector: 'aspect-drop-list-simple', template: ` <div class="list-container"> - <!-- Border width is a workaround to enable/disable the Material cdk-drop-list-receiving--> - <!-- class style.--> + <!-- Border width is a workaround to enable/disable the Material cdk-drop-list-receiving--> + <!-- class style.--> <div class="list" [style.height.px]="elementModel.height" [style.width.px]="elementModel.width" [class.dropList-highlight]="elementModel.highlightReceivingDropList" [style.border-color]="elementModel.highlightReceivingDropListColor" [style.border-width.px]="elementModel.highlightReceivingDropList ? 2 : 0" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''" - [style.backgroundColor]="elementModel.styles.backgroundColor" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''" + [style.backgroundColor]="elementModel.styling.backgroundColor" cdkDropList [id]="elementModel.id" [cdkDropListData]="this" @@ -32,36 +32,37 @@ import { DragNDropValueObject, DropListSimpleElement } from '../../interfaces/el [cdkDropListEnterPredicate]="onlyOneItemPredicate" (cdkDropListDropped)="drop($event)"> <ng-container *ngIf="!parentForm"> - <ng-container *ngFor="let value of $any(elementModel.value)"> - <ng-container [ngTemplateOutlet]="dropObject" [ngTemplateOutletContext]="{ $implicit: value }"> + <ng-container *ngFor="let value of $any(elementModel.value)"> + <ng-container [ngTemplateOutlet]="dropObject" [ngTemplateOutletContext]="{ $implicit: value }"> + </ng-container> </ng-container> - </ng-container> </ng-container> <ng-container *ngIf="parentForm"> - <ng-container *ngFor="let value of elementFormControl.value"> - <ng-container [ngTemplateOutlet]="dropObject" [ngTemplateOutletContext]="{ $implicit: value }"> + <ng-container *ngFor="let value of elementFormControl.value"> + <ng-container [ngTemplateOutlet]="dropObject" [ngTemplateOutletContext]="{ $implicit: value }"> + </ng-container> </ng-container> - </ng-container> </ng-container> <ng-template #dropObject let-value> <div class="item" [style.line-height.px]="elementModel.height - 4" - [style.background-color]="elementModel.styles.itemBackgroundColor" + [style.background-color]="elementModel.styling.itemBackgroundColor" cdkDrag (cdkDragStarted)=dragStart() (cdkDragEnded)="dragEnd()"> <div *cdkDragPreview - [style.font-size.px]="elementModel.styles.fontSize" - [style.background-color]="elementModel.styles.itemBackgroundColor"> + [style.font-size.px]="elementModel.styling.fontSize" + [style.background-color]="elementModel.styling.itemBackgroundColor"> {{value.stringValue}} </div> - <div class="drag-placeholder" *cdkDragPlaceholder [style.min-height.px]="elementModel.styles.fontSize"> + <div class="drag-placeholder" *cdkDragPlaceholder + [style.min-height.px]="elementModel.styling.fontSize"> </div> {{value.stringValue}} </div> - </ng-template> + </ng-template> </div> <mat-error *ngIf="elementFormControl.errors && elementFormControl.touched" class="error-message"> diff --git a/projects/common/components/ui-elements/drop-list.component.ts b/projects/common/components/ui-elements/drop-list.component.ts index 5df6e99c011db9b6d21de06c8d658e065d75ab1e..2f0bea40e2ca77c8115d7da314b87ec54a556b2c 100644 --- a/projects/common/components/ui-elements/drop-list.component.ts +++ b/projects/common/components/ui-elements/drop-list.component.ts @@ -18,13 +18,13 @@ import { DragNDropValueObject, DropListElement } from '../../interfaces/elements [ngClass]="{ 'align-flex' : elementModel.orientation === 'flex' }" [class.dropList-highlight]="elementModel.highlightReceivingDropList" [style.outline-color]="elementModel.highlightReceivingDropListColor" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''" - [style.backgroundColor]="elementModel.styles.backgroundColor" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''" + [style.backgroundColor]="elementModel.styling.backgroundColor" [style.display]="elementModel.orientation === 'horizontal' ? 'flex' : ''" [style.flex-direction]="elementModel.orientation === 'horizontal' ? 'row' : ''" cdkDropList @@ -48,31 +48,32 @@ import { DragNDropValueObject, DropListElement } from '../../interfaces/elements </ng-container> </ng-container> </ng-container> - <!--Leave template within the dom to ensure dragNdrop--> - <ng-template #dropObject let-value> - <div class="item text-item" *ngIf="!value.imgSrcValue" cdkDrag - [ngClass]="{ 'vertical-orientation' : elementModel.orientation === 'vertical', - 'horizontal-orientation' : elementModel.orientation === 'horizontal'}" - [style.background-color]="elementModel.itemBackgroundColor" - (cdkDragStarted)=dragStart() (cdkDragEnded)="dragEnd()"> - <div *cdkDragPreview - [style.font-size.px]="elementModel.styles.fontSize" - [style.background-color]="elementModel.styles.itemBackgroundColor"> + <!--Leave template within the dom to ensure dragNdrop--> + <ng-template #dropObject let-value> + <div class="item text-item" *ngIf="!value.imgSrcValue" cdkDrag + [ngClass]="{ 'vertical-orientation' : elementModel.orientation === 'vertical', + 'horizontal-orientation' : elementModel.orientation === 'horizontal'}" + [style.background-color]="elementModel.itemBackgroundColor" + (cdkDragStarted)=dragStart() (cdkDragEnded)="dragEnd()"> + <div *cdkDragPreview + [style.font-size.px]="elementModel.styling.fontSize" + [style.background-color]="elementModel.styling.itemBackgroundColor"> {{value.stringValue}} - </div> - <div class="drag-placeholder" *cdkDragPlaceholder [style.min-height.px]="elementModel.styles.fontSize"> - </div> - {{value.stringValue}} </div> - <img *ngIf="value.imgSrcValue" - [src]="value.imgSrcValue | safeResourceUrl" alt="Image Placeholder" - [style.display]="elementModel.orientation === 'flex' ? '' : 'block'" - class="item" - [ngClass]="{ 'vertical-orientation' : elementModel.orientation === 'vertical', - 'horizontal-orientation' : elementModel.orientation === 'horizontal'}" - cdkDrag (cdkDragStarted)=dragStart() (cdkDragEnded)="dragEnd()" - [style.object-fit]="'scale-down'"> - </ng-template> + <div class="drag-placeholder" *cdkDragPlaceholder + [style.min-height.px]="elementModel.styling.fontSize"> + </div> + {{value.stringValue}} + </div> + <img *ngIf="value.imgSrcValue" + [src]="value.imgSrcValue | safeResourceUrl" alt="Image Placeholder" + [style.display]="elementModel.orientation === 'flex' ? '' : 'block'" + class="item" + [ngClass]="{ 'vertical-orientation' : elementModel.orientation === 'vertical', + 'horizontal-orientation' : elementModel.orientation === 'horizontal'}" + cdkDrag (cdkDragStarted)=dragStart() (cdkDragEnded)="dragEnd()" + [style.object-fit]="'scale-down'"> + </ng-template> </div> <mat-error *ngIf="elementFormControl.errors && elementFormControl.touched" class="error-message"> diff --git a/projects/common/components/ui-elements/dropdown.component.ts b/projects/common/components/ui-elements/dropdown.component.ts index 49db99cf1c7b22b3da24da3caa734da7393da95e..e6118c65a16db6d931f1cbd364679ad6b32dbb9c 100644 --- a/projects/common/components/ui-elements/dropdown.component.ts +++ b/projects/common/components/ui-elements/dropdown.component.ts @@ -6,16 +6,16 @@ import { DropdownElement } from '../../interfaces/elements'; selector: 'aspect-dropdown', template: ` <mat-form-field - appearance="fill" - [style.width.%]="100" - [style.height.%]="100" - aspectInputBackgroundColor [backgroundColor]="elementModel.styles.backgroundColor"> - <mat-label [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''"> + appearance="fill" + [style.width.%]="100" + [style.height.%]="100" + aspectInputBackgroundColor [backgroundColor]="elementModel.styling.backgroundColor"> + <mat-label [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"> {{$any(elementModel).label}} </mat-label> <mat-select [formControl]="elementFormControl" [value]="elementModel.value"> diff --git a/projects/common/components/ui-elements/frame.component.ts b/projects/common/components/ui-elements/frame.component.ts index e3fc782a569be24ef7f7dfce63f17ede0d9b1c41..4cada7b076abcdcaa0312349ba860c43beec4ca5 100644 --- a/projects/common/components/ui-elements/frame.component.ts +++ b/projects/common/components/ui-elements/frame.component.ts @@ -5,13 +5,13 @@ import { FrameElement } from '../../interfaces/elements'; @Component({ selector: 'aspect-frame', template: ` - <div [style.width]="'calc(100% - ' + (elementModel.styles.borderWidth * 2) + 'px)'" - [style.height]="'calc(100% - ' + (elementModel.styles.borderWidth * 2) + 'px)'" - [style.border-style]="elementModel.styles.borderStyle" - [style.border-width.px]="elementModel.styles.borderWidth" - [style.border-color]="elementModel.styles.borderColor" - [style.border-radius.px]="elementModel.styles.borderRadius" - [style.background-color]="elementModel.styles.backgroundColor"> + <div [style.width]="'calc(100% - ' + (elementModel.styling.borderWidth * 2) + 'px)'" + [style.height]="'calc(100% - ' + (elementModel.styling.borderWidth * 2) + 'px)'" + [style.border-style]="elementModel.styling.borderStyle" + [style.border-width.px]="elementModel.styling.borderWidth" + [style.border-color]="elementModel.styling.borderColor" + [style.border-radius.px]="elementModel.styling.borderRadius" + [style.background-color]="elementModel.styling.backgroundColor"> </div> ` }) diff --git a/projects/common/components/ui-elements/likert.component.ts b/projects/common/components/ui-elements/likert.component.ts index bb9fbfa88e41a22de7b9b49a6c65cc03e43974ff..d5aa2064f0a40caa2e8fce41dda8f282c854a260 100644 --- a/projects/common/components/ui-elements/likert.component.ts +++ b/projects/common/components/ui-elements/likert.component.ts @@ -17,15 +17,15 @@ import { LikertElement, LikertRowElement } from '../../interfaces/elements'; <div class="mat-typography" [style.display]="'grid'" [style.grid-template-columns]="elementModel.firstColumnSizeRatio + 'fr ' + - '1fr '.repeat(elementModel.columns.length)" - [style.background-color]="elementModel.styles.backgroundColor" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.line-height.%]="elementModel.styles.lineHeight" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''"> + '1fr '.repeat(elementModel.columns.length)" + [style.background-color]="elementModel.styling.backgroundColor" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.line-height.%]="elementModel.styling.lineHeight" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"> <div *ngFor="let column of elementModel.columns; let i = index" class="columns" fxLayout="column" fxLayoutAlign="end center" [style.grid-column-start]="2 + i" @@ -43,16 +43,16 @@ import { LikertElement, LikertRowElement } from '../../interfaces/elements'; <ng-container *ngFor="let row of elementModel.rows; let i = index"> <aspect-likert-radio-button-group - [style.background-color]="elementModel.lineColoring && i % 2 === 0 ? - elementModel.lineColoringColor : ''" - [style.grid-column-start]="1" - [style.grid-column-end]="elementModel.columns.length + 2" - [style.grid-row-start]="2 + i" - [style.grid-row-end]="3 + i" - [style.padding.px]="3" - [elementModel]="row" - [firstColumnSizeRatio]="elementModel.firstColumnSizeRatio" - [parentForm]="parentForm"> + [style.background-color]="elementModel.lineColoring && i % 2 === 0 ? + elementModel.lineColoringColor : ''" + [style.grid-column-start]="1" + [style.grid-column-end]="elementModel.columns.length + 2" + [style.grid-row-start]="2 + i" + [style.grid-row-end]="3 + i" + [style.padding.px]="3" + [elementModel]="row" + [firstColumnSizeRatio]="elementModel.firstColumnSizeRatio" + [parentForm]="parentForm"> </aspect-likert-radio-button-group> </ng-container> </div> diff --git a/projects/common/components/ui-elements/radio-button-group.component.ts b/projects/common/components/ui-elements/radio-button-group.component.ts index 09519cadeb03e4951741f98e3f32708e6594df7e..14a67a2ba306f9fb22de1ab7d725e19bb012475f 100644 --- a/projects/common/components/ui-elements/radio-button-group.component.ts +++ b/projects/common/components/ui-elements/radio-button-group.component.ts @@ -8,13 +8,13 @@ import { RadioButtonGroupElement } from '../../interfaces/elements'; <div class="mat-form-field" [style.width.%]="100" [style.height.%]="100" - [style.background-color]="elementModel.styles.backgroundColor" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''"> + [style.background-color]="elementModel.styling.backgroundColor" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"> <label id="radio-group-label" [innerHTML]="elementModel.label"> </label> @@ -29,7 +29,7 @@ import { RadioButtonGroupElement } from '../../interfaces/elements'; elementFormControl.value !== i }" [value]="i" [style.pointer-events]="elementModel.readOnly ? 'none' : 'unset'" - [style.line-height.%]="elementModel.styles.lineHeight"> + [style.line-height.%]="elementModel.styling.lineHeight"> {{option}} </mat-radio-button> <mat-error *ngIf="elementFormControl.errors && elementFormControl.touched" diff --git a/projects/common/components/ui-elements/radio-group-images.component.ts b/projects/common/components/ui-elements/radio-group-images.component.ts index 5f3739fb3b137e7c6cc25a40b0ba2be531218ec3..abf35cf163e4aba1020b2b6a07d984b0d281b1ac 100644 --- a/projects/common/components/ui-elements/radio-group-images.component.ts +++ b/projects/common/components/ui-elements/radio-group-images.component.ts @@ -9,13 +9,13 @@ import { RadioButtonGroupComplexElement } from '../../interfaces/elements'; [style.height.%]="100" [style.display]="'grid !important'" [style.grid-template-columns]="'1fr '.repeat(elementModel.columns.length)" - [style.background-color]="elementModel.styles.backgroundColor" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''"> + [style.background-color]="elementModel.styling.backgroundColor" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"> <label id="radio-group-label" class="label" [style.grid-column-start]="1" [style.grid-column-end]="2 + elementModel.columns.length" diff --git a/projects/common/components/ui-elements/slider.component.ts b/projects/common/components/ui-elements/slider.component.ts index b840d4b45bc5263aa073a5b796932b2a1afc5482..cd395eef7447b4c29564c7ba3e8d866579b98623 100644 --- a/projects/common/components/ui-elements/slider.component.ts +++ b/projects/common/components/ui-elements/slider.component.ts @@ -11,16 +11,16 @@ import { SliderElement } from '../../interfaces/elements'; <div fxLayout="column" [style.width.%]="100" [style.height.%]="100" - [style.background-color]="elementModel.styles.backgroundColor"> + [style.background-color]="elementModel.styling.backgroundColor"> <div *ngIf="elementModel.label" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.line-height.%]="elementModel.styles.lineHeight" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''"> - {{elementModel.label}} + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.line-height.%]="elementModel.styling.lineHeight" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"> + {{elementModel.label}} </div> <div #valueContainer [class.values]="elementModel.label && !elementModel.showValues"> @@ -29,36 +29,34 @@ import { SliderElement } from '../../interfaces/elements'; class="value-container-min"> <div *ngIf="elementModel.showValues" class="value-container"> - <div - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.line-height.%]="elementModel.styles.lineHeight" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''"> - {{elementModel.minValue | number:'':'de'}} - </div> - <div *ngIf="elementModel.barStyle" class="number-marker"></div> + <div [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.line-height.%]="elementModel.styling.lineHeight" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"> + {{elementModel.minValue | number:'':'de'}} + </div> + <div *ngIf="elementModel.barStyle" class="number-marker"></div> </div> </div> <div #valueMax [class.value-container-max]="elementModel.barStyle"> <div *ngIf="elementModel.showValues" class="value-container"> - <div - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.line-height.%]="elementModel.styles.lineHeight" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''"> - {{elementModel.maxValue | number:'':'de'}} - </div> - <div *ngIf="elementModel.barStyle" - class="number-marker"> - </div> + <div [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.line-height.%]="elementModel.styling.lineHeight" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''"> + {{elementModel.maxValue | number:'':'de'}} + </div> + <div *ngIf="elementModel.barStyle" + class="number-marker"> + </div> </div> </div> </div> @@ -77,13 +75,12 @@ import { SliderElement } from '../../interfaces/elements'; [style.margin-right.px]="elementModel.barStyle ? valueMax.offsetWidth/2 - 8 : valueMax.offsetWidth" [style.margin-left.px]="elementModel.barStyle ? valueMin.offsetWidth/2 - 8: valueMin.offsetWidth" [style.margin-top.px]="elementModel.barStyle ? -32 : -valueContainer.offsetHeight"> - <mat-slider - [class]="elementModel.barStyle ? 'bar-style' : ''" - [thumbLabel]="elementModel.thumbLabel" - [formControl]="elementFormControl" - [style.width.%]="100" - [max]="elementModel.maxValue" - [min]="elementModel.minValue"> + <mat-slider [class]="elementModel.barStyle ? 'bar-style' : ''" + [thumbLabel]="elementModel.thumbLabel" + [formControl]="elementFormControl" + [style.width.%]="100" + [max]="elementModel.maxValue" + [min]="elementModel.minValue"> </mat-slider> <mat-error *ngIf="elementFormControl.touched && elementFormControl.errors"> {{elementModel.requiredWarnMessage}} diff --git a/projects/common/components/ui-elements/spell-correct.component.ts b/projects/common/components/ui-elements/spell-correct.component.ts index cb7c0f8bf7126114b2f7f4b3a84de89139945043..90b82cda2c0a6c773bf69ab763cb8f6412f346dc 100644 --- a/projects/common/components/ui-elements/spell-correct.component.ts +++ b/projects/common/components/ui-elements/spell-correct.component.ts @@ -8,9 +8,8 @@ import { SpellCorrectElement } from '../../interfaces/elements'; template: ` <div [style.width.%]="100" [style.height.%]="100"> - <div fxFlex - fxLayout="column" - aspectInputBackgroundColor [backgroundColor]="elementModel.styles.backgroundColor" + <div fxFlex fxLayout="column" + aspectInputBackgroundColor [backgroundColor]="elementModel.styling.backgroundColor" [style.width.%]="100" [style.height.%]="100"> <mat-form-field class="small-input"> @@ -18,12 +17,12 @@ import { SpellCorrectElement } from '../../interfaces/elements'; [style.text-align]="'center'" autocomplete="off" [readonly]="elementModel.readOnly" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''" [value]="elementModel.value" [formControl]="elementFormControl"> </mat-form-field> @@ -31,25 +30,23 @@ import { SpellCorrectElement } from '../../interfaces/elements'; mat-button type="button" [disabled]="elementModel.readOnly" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : '400'" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : '400'" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''" [style.width.%]="100" [style.margin-top]="'-20px'" - [style.text-decoration-line]= - "(inputElement && inputElement.focused) || - (inputElement && !!inputElement.value) || - (elementFormControl && elementFormControl.value === '') ? 'line-through' : ''" - (click)=" - elementFormControl.value === null ? - inputElement.focus() : - buttonElement.focus(); - elementFormControl.value === null ? - (elementFormControl.setValue('')) : - elementFormControl.setValue(null)">{{elementModel.label}} + [style.text-decoration-line]="(inputElement && inputElement.focused) || + (inputElement && !!inputElement.value) || + (elementFormControl && elementFormControl.value === '') ? 'line-through' : ''" + (click)="elementFormControl.value === null ? + inputElement.focus() : + buttonElement.focus(); + elementFormControl.value === null ? + (elementFormControl.setValue('')) : + elementFormControl.setValue(null)">{{elementModel.label}} </button> </div> </div> diff --git a/projects/common/components/ui-elements/text-area.component.ts b/projects/common/components/ui-elements/text-area.component.ts index 53750e2c58941b077d6e683ce97483814a771959..cf346635e86f55df3cfeeb3ee63abf8f4b429249 100644 --- a/projects/common/components/ui-elements/text-area.component.ts +++ b/projects/common/components/ui-elements/text-area.component.ts @@ -12,13 +12,13 @@ import { TextAreaElement } from '../../interfaces/elements'; [style.width.%]="100" [style.height.%]="100" [style.min-height.%]="100" - aspectInputBackgroundColor [backgroundColor]="elementModel.styles.backgroundColor" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''" + aspectInputBackgroundColor [backgroundColor]="elementModel.styling.backgroundColor" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''" [appearance]="$any(elementModel.appearance)"> <mat-label *ngIf="elementModel.label">{{elementModel.label}}</mat-label> <textarea matInput #input @@ -27,7 +27,7 @@ import { TextAreaElement } from '../../interfaces/elements'; [value]="elementModel.value" [readonly]="elementModel.readOnly" [style.min-width.%]="100" - [style.line-height.%]="elementModel.styles.lineHeight" + [style.line-height.%]="elementModel.styling.lineHeight" [style.resize]="elementModel.resizeEnabled ? 'both' : 'none'" (focus)="elementModel.inputAssistancePreset !== 'none' ? onFocusChanged.emit(input) : null" (blur)="elementModel.inputAssistancePreset !== 'none' ? onFocusChanged.emit(null): null"> diff --git a/projects/common/components/ui-elements/text-field-simple.component.ts b/projects/common/components/ui-elements/text-field-simple.component.ts index f1b539910a4c9d3b8a531daecb65fff8864b1455..621faa31d513035f5688a6f36e7414aa36840a50 100644 --- a/projects/common/components/ui-elements/text-field-simple.component.ts +++ b/projects/common/components/ui-elements/text-field-simple.component.ts @@ -8,13 +8,13 @@ import { TextFieldSimpleElement } from '../../interfaces/elements'; <input type="text" form="parentForm" [style.width.px]="elementModel.width" [style.height.px]="elementModel.height" - [style.line-height.px]="elementModel.styles.fontSize" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''" + [style.line-height.px]="elementModel.styling.fontSize" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''" [readonly]="elementModel.readOnly" [formControl]="elementFormControl" [value]="elementModel.value"> diff --git a/projects/common/components/ui-elements/text-field.component.ts b/projects/common/components/ui-elements/text-field.component.ts index 90885d18e4968c5bc6595996df3a5de00583860b..4743da1987defbf6c53004d6779e7d5da7df656e 100644 --- a/projects/common/components/ui-elements/text-field.component.ts +++ b/projects/common/components/ui-elements/text-field.component.ts @@ -8,17 +8,17 @@ import { TextFieldElement } from '../../interfaces/elements'; selector: 'aspect-text-field', template: ` <mat-form-field - *ngIf="elementModel.label !== ''" - [style.width.%]="100" - [style.height.%]="100" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''" - aspectInputBackgroundColor [backgroundColor]="elementModel.styles.backgroundColor" - [appearance]="$any(elementModel.appearance)"> + *ngIf="elementModel.label !== ''" + [style.width.%]="100" + [style.height.%]="100" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''" + aspectInputBackgroundColor [backgroundColor]="elementModel.styling.backgroundColor" + [appearance]="$any(elementModel.appearance)"> <mat-label>{{elementModel.label}}</mat-label> <input matInput type="text" #input autocomplete="off" [formControl]="elementFormControl" @@ -37,18 +37,17 @@ import { TextFieldElement } from '../../interfaces/elements'; {{elementFormControl.errors | errorTransform: elementModel}} </mat-error> </mat-form-field> - <mat-form-field - *ngIf="elementModel.label === ''" class="small-input" - [style.width.%]="100" - [style.height.%]="100" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''" - aspectInputBackgroundColor [backgroundColor]="elementModel.styles.backgroundColor" - [appearance]="$any(elementModel.appearance)"> + <mat-form-field *ngIf="elementModel.label === ''" class="small-input" + [style.width.%]="100" + [style.height.%]="100" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''" + aspectInputBackgroundColor [backgroundColor]="elementModel.styling.backgroundColor" + [appearance]="$any(elementModel.appearance)"> <input matInput type="text" #input autocomplete="off" [formControl]="elementFormControl" [value]="elementModel.value" diff --git a/projects/common/components/ui-elements/text.component.ts b/projects/common/components/ui-elements/text.component.ts index 6587db6feef36dd991f7c6012a26bdf4b6e5d761..5c8ce80211234e4e5e10cdcffcea191d6fc7d21d 100644 --- a/projects/common/components/ui-elements/text.component.ts +++ b/projects/common/components/ui-elements/text.component.ts @@ -9,30 +9,30 @@ import { TextElement, ValueChangeElement } from '../../interfaces/elements'; template: ` <div [style.width.%]="100" [style.height.%]="100"> - <aspect-marking-bar - *ngIf="elementModel.highlightableYellow || - elementModel.highlightableTurquoise || - elementModel.highlightableOrange" - [elementModel]="elementModel" - (selectionChanged)="onSelectionChanged($event)"> - </aspect-marking-bar> - <div #textContainerRef class="text-container" - [class.orange-selection]="selectedColor === 'orange'" - [class.yellow-selection]="selectedColor === 'yellow'" - [class.turquoise-selection]="selectedColor === 'turquoise'" - [class.delete-selection]="selectedColor === 'delete'" - [style.background-color]="elementModel.styles.backgroundColor" - [style.color]="elementModel.styles.fontColor" - [style.font-family]="elementModel.styles.font" - [style.font-size.px]="elementModel.styles.fontSize" - [style.line-height.%]="elementModel.styles.lineHeight" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''" - [innerHTML]="savedText || elementModel.text | safeResourceHTML" - (touchstart)="emitStartSelection($event)" - (mousedown)="emitStartSelection($event)"> - </div> + <aspect-marking-bar + *ngIf="elementModel.highlightableYellow || + elementModel.highlightableTurquoise || + elementModel.highlightableOrange" + [elementModel]="elementModel" + (selectionChanged)="onSelectionChanged($event)"> + </aspect-marking-bar> + <div #textContainerRef class="text-container" + [class.orange-selection]="selectedColor === 'orange'" + [class.yellow-selection]="selectedColor === 'yellow'" + [class.turquoise-selection]="selectedColor === 'turquoise'" + [class.delete-selection]="selectedColor === 'delete'" + [style.background-color]="elementModel.styling.backgroundColor" + [style.color]="elementModel.styling.fontColor" + [style.font-family]="elementModel.styling.font" + [style.font-size.px]="elementModel.styling.fontSize" + [style.line-height.%]="elementModel.styling.lineHeight" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''" + [innerHTML]="savedText || elementModel.text | safeResourceHTML" + (touchstart)="emitStartSelection($event)" + (mousedown)="emitStartSelection($event)"> + </div> </div> `, styles: [ diff --git a/projects/common/components/ui-elements/toggle-button.component.ts b/projects/common/components/ui-elements/toggle-button.component.ts index 2f332c564293fb5b117321de0c42967ebf0648bf..c81b1970edf01faa47b5f0e5e8c1ce37eb9cb85d 100644 --- a/projects/common/components/ui-elements/toggle-button.component.ts +++ b/projects/common/components/ui-elements/toggle-button.component.ts @@ -16,17 +16,17 @@ import { ToggleButtonElement } from '../../interfaces/elements'; [ngClass]="{ 'strike' : elementModel.strikeOtherOptions && elementFormControl.value !== null && elementFormControl.value !== i }" - [style.color]="elementModel.styles.fontColor" - [style.font-size.px]="elementModel.styles.fontSize" - [style.font-weight]="elementModel.styles.bold ? 'bold' : ''" - [style.font-style]="elementModel.styles.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.styles.underline ? 'underline' : ''" - [style.font-family]="elementModel.styles.font" + [style.color]="elementModel.styling.fontColor" + [style.font-size.px]="elementModel.styling.fontSize" + [style.font-weight]="elementModel.styling.bold ? 'bold' : ''" + [style.font-style]="elementModel.styling.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''" + [style.font-family]="elementModel.styling.font" [style.background-color]="elementFormControl.value !== null && elementFormControl.value === i ? elementModel.selectionColor : - elementModel.styles.backgroundColor" - [style.line-height.%]="elementModel.styles.lineHeight"> + elementModel.styling.backgroundColor" + [style.line-height.%]="elementModel.styling.lineHeight"> <!--Background color does not show in editor--> {{option}} </mat-button-toggle> diff --git a/projects/common/components/ui-elements/video.component.ts b/projects/common/components/ui-elements/video.component.ts index a46faf9bb2adca05d9a96cc29903e0bdc8ed35e3..e5813e49aaa818bb463bcdb36aa89aad28596829 100644 --- a/projects/common/components/ui-elements/video.component.ts +++ b/projects/common/components/ui-elements/video.component.ts @@ -20,7 +20,7 @@ import { VideoElement } from '../../interfaces/elements'; [active]="active" [id]="elementModel.id" [savedPlaybackTime]="savedPlaybackTime" - [playerProperties]="elementModel.playerProps" + [playerProperties]="elementModel.player" [dependencyDissolved]="dependencyDissolved" (onMediaValidStatusChanged)="onMediaValidStatusChanged.emit($event)" (elementValueChanged)="elementValueChanged.emit($event)"> diff --git a/projects/common/directives/media-player-element-component.directive.ts b/projects/common/directives/media-player-element-component.directive.ts index 051ed225ef347d5d3fdc9a225538c39bef07bd4e..a6ee7b18f2fd8ca1d83c639666adb1dd19cb13c1 100644 --- a/projects/common/directives/media-player-element-component.directive.ts +++ b/projects/common/directives/media-player-element-component.directive.ts @@ -35,7 +35,7 @@ export abstract class MediaPlayerElementComponent extends ElementComponent imple private setActivatedAfterID(id: string): void { if (!this.dependencyDissolved) { - this.dependencyDissolved = id === (this.elementModel).playerProps.activeAfterID; + this.dependencyDissolved = id === (this.elementModel).player.activeAfterID; } } } diff --git a/projects/common/interfaces/elements.ts b/projects/common/interfaces/elements.ts index f1c1e218c9bbb2339a9c711f09223eef39077a62..c5260db341ee740a2fef5e0267cc59380f319518 100644 --- a/projects/common/interfaces/elements.ts +++ b/projects/common/interfaces/elements.ts @@ -4,7 +4,7 @@ import { LikertColumn } from './likert'; export type InputElementValue = string[] | string | number | boolean | DragNDropValueObject[] | null; export type UIElementType = 'text' | 'button' | 'text-field' | 'text-area' | 'checkbox' | 'dropdown' | 'radio' | 'image' | 'audio' | 'video' | 'likert' | 'likert-row' | 'radio-group-images' -| 'drop-list' | 'cloze' | 'spell-correct' | 'slider' | 'frame' | 'toggle-button'; +| 'drop-list' | 'cloze' | 'spell-correct' | 'slider' | 'frame' | 'toggle-button' | 'text-field-simple'; export type InputAssistancePreset = 'none' | 'french' | 'numbers' | 'numbersAndOperators' | 'numbersAndBasicOperators' | 'comparisonOperators' | 'squareDashDot' | 'placeValue'; export type DragNDropValueObject = { @@ -14,7 +14,7 @@ export type DragNDropValueObject = { }; export type UIElementValue = string | number | boolean | undefined | UIElementType | InputElementValue | LikertColumn[] | ClozeDocument | -PositionProperties | ElementStyles | PlayerProperties | BasicStyles; +PositionProperties | ElementStyling | PlayerProperties | BasicStyles; export interface UIElement { [index: string]: UIElementValue; @@ -22,9 +22,9 @@ export interface UIElement { id: string; width: number; height: number; - positionProps?: PositionProperties; - styles: ElementStyles; - playerProps?: PlayerProperties; + position?: PositionProperties; // position + styling?: ElementStyling; // styling + player?: PlayerProperties; // player } export interface InputElement extends UIElement { @@ -36,11 +36,11 @@ export interface InputElement extends UIElement { } export interface PositionedElement extends UIElement { - positionProps: PositionProperties; + position: PositionProperties; } export interface PlayerElement extends UIElement { - playerProps: PlayerProperties; + player: PlayerProperties; } export interface ValueChangeElement { @@ -66,7 +66,7 @@ export interface PositionProperties { zIndex: number; } -export interface ElementStyles { +export interface ElementStyling { [index: string]: string | number | boolean | undefined; fontColor?: string; font?: string; @@ -85,7 +85,7 @@ export interface ElementStyles { lineColoringColor?: string; } -export interface BasicStyles extends ElementStyles { +export interface BasicStyles extends ElementStyling { fontColor: string; font: string; fontSize: number; @@ -125,23 +125,23 @@ export interface ButtonElement extends UIElement { imageSrc: string | null; action: null | 'unitNav' | 'pageNav' actionParam: null | 'previous' | 'next' | 'first' | 'last' | 'end' | number; - positionProps: PositionProperties; - styles: BasicStyles & { + position: PositionProperties; + styling: BasicStyles & { borderRadius: number; } } export interface CheckboxElement extends InputElement { type: 'checkbox'; - positionProps: PositionProperties; - styles: BasicStyles; + position: PositionProperties; + styling: BasicStyles; } export interface ClozeElement extends UIElement { type: 'cloze'; document: ClozeDocument; - positionProps: PositionProperties; - styles: BasicStyles & { + position: PositionProperties; + styling: BasicStyles & { lineHeight: number; } } @@ -150,8 +150,8 @@ export interface DropdownElement extends InputElement { type: 'dropdown'; options: string[]; allowUnset: boolean; - positionProps: PositionProperties; - styles: BasicStyles + position: PositionProperties; + styling: BasicStyles } export interface DropListElement extends InputElement { @@ -161,8 +161,8 @@ export interface DropListElement extends InputElement { orientation: 'vertical' | 'horizontal' | 'flex'; highlightReceivingDropList: boolean; highlightReceivingDropListColor: string; - positionProps: PositionProperties; - styles: BasicStyles & { + position: PositionProperties; + styling: BasicStyles & { itemBackgroundColor: string; } } @@ -172,15 +172,15 @@ export interface DropListSimpleElement extends InputElement { connectedTo: string[]; highlightReceivingDropList: boolean; highlightReceivingDropListColor: string; - styles: BasicStyles & { + styling: BasicStyles & { itemBackgroundColor: string; } } export interface FrameElement extends UIElement { type: 'frame'; - positionProps: PositionProperties; - styles: BasicStyles & { + position: PositionProperties; + styling: BasicStyles & { borderWidth: number; borderColor: string; borderStyle: 'solid' | 'dotted' | 'dashed' | 'double' | 'groove' | 'ridge' | 'inset' | 'outset'; @@ -196,7 +196,7 @@ export interface ImageElement extends UIElement { magnifierSize: number; magnifierZoom: number; magnifierUsed: boolean; - positionProps: PositionProperties; + position: PositionProperties; } export interface LikertElement extends UIElement { @@ -205,8 +205,8 @@ export interface LikertElement extends UIElement { columns: LikertColumn[]; firstColumnSizeRatio: number; readOnly: boolean; - positionProps: PositionProperties; - styles: BasicStyles & { + position: PositionProperties; + styling: BasicStyles & { lineHeight: number; lineColoring: boolean; lineColoringColor: string; @@ -225,15 +225,15 @@ export interface RadioButtonGroupElement extends InputElement { options: string[]; alignment: 'column' | 'row'; strikeOtherOptions: boolean; - positionProps: PositionProperties; - styles: BasicStyles; + position: PositionProperties; + styling: BasicStyles; } export interface RadioButtonGroupComplexElement extends InputElement { type: 'radio-group-images' // TODO better name columns: LikertColumn[]; - positionProps: PositionProperties; - styles: BasicStyles; + position: PositionProperties; + styling: BasicStyles; } export interface SliderElement extends InputElement { @@ -243,14 +243,14 @@ export interface SliderElement extends InputElement { showValues: boolean; barStyle: boolean; // TODO besserer name thumbLabel: boolean; - positionProps: PositionProperties; - styles: BasicStyles; + position: PositionProperties; + styling: BasicStyles; } export interface SpellCorrectElement extends InputElement { type: 'spell-correct'; - positionProps: PositionProperties; - styles: BasicStyles; + position: PositionProperties; + styling: BasicStyles; } export interface TextFieldElement extends InputElement { @@ -265,8 +265,8 @@ export interface TextFieldElement extends InputElement { inputAssistancePreset: InputAssistancePreset; inputAssistancePosition: 'floating' | 'right'; clearable: boolean; - positionProps: PositionProperties; - styles: BasicStyles; + position: PositionProperties; + styling: BasicStyles; } export interface TextAreaElement extends InputElement { @@ -276,15 +276,15 @@ export interface TextAreaElement extends InputElement { rowCount: number; inputAssistancePreset: InputAssistancePreset; inputAssistancePosition: 'floating' | 'right'; - positionProps: PositionProperties; - styles: BasicStyles & { + position: PositionProperties; + styling: BasicStyles & { lineHeight: number; }; } export interface TextFieldSimpleElement extends InputElement { type: 'text-field'; - styles: BasicStyles; // TODO okay? bg-color? + styling: BasicStyles; // TODO okay? bg-color? } export interface TextElement extends UIElement { @@ -293,8 +293,8 @@ export interface TextElement extends UIElement { highlightableOrange: boolean; highlightableTurquoise: boolean; highlightableYellow: boolean; - positionProps: PositionProperties; - styles: BasicStyles & { + position: PositionProperties; + styling: BasicStyles & { lineHeight: number; } } @@ -306,7 +306,7 @@ export interface ToggleButtonElement extends InputElement { selectionColor: string; verticalOrientation: boolean; dynamicWidth: boolean; - styles: BasicStyles & { + styling: BasicStyles & { lineHeight: number; }; } @@ -314,14 +314,14 @@ export interface ToggleButtonElement extends InputElement { export interface AudioElement extends UIElement { type: 'audio'; src: string; - positionProps: PositionProperties; - playerProps: PlayerProperties; + position: PositionProperties; + player: PlayerProperties; } export interface VideoElement extends UIElement { type: 'video'; src: string; scale: boolean; // TODO besserer name - positionProps: PositionProperties; - playerProps: PlayerProperties; + position: PositionProperties; + player: PlayerProperties; } diff --git a/projects/common/services/message.service.ts b/projects/common/services/message.service.ts index b2db689927ccf12d0efd856c3dccb80de767d2d1..b8f1fcab992d937fa271555a6770c77d82039bf0 100644 --- a/projects/common/services/message.service.ts +++ b/projects/common/services/message.service.ts @@ -5,7 +5,15 @@ import { MatSnackBar } from '@angular/material/snack-bar'; providedIn: 'root' }) export class MessageService { - constructor(private _snackBar: MatSnackBar) { } + private static instance: MessageService; + + constructor(private _snackBar: MatSnackBar) { + MessageService.instance = this; + } + + static getInstance(): MessageService { + return MessageService.instance; + } showWarning(text: string): void { this._snackBar.open(text, undefined, { duration: 2000, panelClass: 'snackbar-warning' }); diff --git a/projects/common/tiptap-editor-extensions/drop-list.ts b/projects/common/tiptap-editor-extensions/drop-list.ts index c0fab8782a53424043b2da2c73a3f29ff3e3c360..dc4c4b8bcf43372cfad2d2276de1e6d4d702f6e0 100644 --- a/projects/common/tiptap-editor-extensions/drop-list.ts +++ b/projects/common/tiptap-editor-extensions/drop-list.ts @@ -10,7 +10,7 @@ const DropListExtension = addAttributes() { return { model: { - default: ElementFactory.createElement('drop-list', { height: 25, width: 100 }) + default: ElementFactory.createElement({ type: 'drop-list', height: 25, width: 100 }) } }; }, diff --git a/projects/common/tiptap-editor-extensions/text-field.ts b/projects/common/tiptap-editor-extensions/text-field.ts index a6e41f9d7741a34ed421903fd583b5e70905a53d..599c04e35ea4b9b7469e55fbf61a05a39f0fadd5 100644 --- a/projects/common/tiptap-editor-extensions/text-field.ts +++ b/projects/common/tiptap-editor-extensions/text-field.ts @@ -10,7 +10,7 @@ const TextFieldExtension = addAttributes() { return { model: { - default: ElementFactory.createElement('text-field-simple') + default: ElementFactory.createElement({ type: 'text-field-simple' }) } }; }, diff --git a/projects/common/tiptap-editor-extensions/toggle-button.ts b/projects/common/tiptap-editor-extensions/toggle-button.ts index 18b62121de8838a365be4d6bb7220737a5834c4e..80144f537fccbd0749cbd7765717a17fc337e8ed 100644 --- a/projects/common/tiptap-editor-extensions/toggle-button.ts +++ b/projects/common/tiptap-editor-extensions/toggle-button.ts @@ -10,7 +10,7 @@ const ToggleButtonExtension = addAttributes() { return { model: { - default: ElementFactory.createElement('toggle-button') + default: ElementFactory.createElement({ type: 'toggle-button' }) } }; }, diff --git a/projects/common/util/element.factory.ts b/projects/common/util/element.factory.ts index 6c44380d81abb624bebe83fc24d5d8c0b408fc2c..e220dd50129b6c8dbdb5f9134a99604959aab5be 100644 --- a/projects/common/util/element.factory.ts +++ b/projects/common/util/element.factory.ts @@ -24,139 +24,145 @@ import { ClozeElement, DropdownElement, DropListElement, - DropListSimpleElement, + DropListSimpleElement, ElementStyling, FrameElement, ImageElement, - InputElement, LikertElement, LikertRowElement, PlayerProperties, PositionProperties, + InputElement, InputElementValue, LikertElement, LikertRowElement, PlayerProperties, PositionProperties, RadioButtonGroupComplexElement, RadioButtonGroupElement, SliderElement, SpellCorrectElement, TextAreaElement, TextElement, TextFieldElement, TextFieldSimpleElement, ToggleButtonElement, - UIElement, UIElementType, + UIElement, UIElementType, UIElementValue, VideoElement } from '../interfaces/elements'; export abstract class ElementFactory { - static createElement(elementType: string, defaults?: Record<string, any>): UIElement { - switch (elementType) { + static createElement(element: Partial<UIElement>): UIElement { + // console.log('createElement', element); + switch (element.type) { case 'text': - return ElementFactory.createTextElement(defaults); + return ElementFactory.createTextElement(element as Partial<TextElement>); case 'button': - return ElementFactory.createButtonElement(defaults); + return ElementFactory.createButtonElement(element as Partial<ButtonElement>); case 'text-field': - return ElementFactory.createTextFieldElement(defaults); + return ElementFactory.createTextFieldElement(element as Partial<TextFieldElement>); case 'text-field-simple': - return ElementFactory.createTextFieldSimpleElement(defaults); + return ElementFactory.createTextFieldSimpleElement(element as Partial<TextFieldSimpleElement>); case 'text-area': - return ElementFactory.createTextAreaElement(defaults); + return ElementFactory.createTextAreaElement(element as Partial<TextAreaElement>); case 'checkbox': - return ElementFactory.createCheckboxElement(defaults); + return ElementFactory.createCheckboxElement(element as Partial<CheckboxElement>); case 'dropdown': - return ElementFactory.createDropdownElement(defaults); + return ElementFactory.createDropdownElement(element as Partial<DropdownElement>); case 'radio': - return ElementFactory.createRadioButtonGroupElement(defaults); + return ElementFactory.createRadioButtonGroupElement(element as Partial<RadioButtonGroupElement>); case 'image': - return ElementFactory.createImageElement(defaults); + return ElementFactory.createImageElement(element as Partial<ImageElement>); case 'audio': - return ElementFactory.createAudioElement(defaults); + return ElementFactory.createAudioElement(element as Partial<AudioElement>); case 'video': - return ElementFactory.createVideoElement(defaults); + return ElementFactory.createVideoElement(element as Partial<VideoElement>); case 'likert': - return ElementFactory.createLikertElement(defaults); + return ElementFactory.createLikertElement(element as Partial<LikertElement>); case 'radio-group-images': - return ElementFactory.createRadioButtonGroupComplexElement(defaults); + return ElementFactory.createRadioButtonGroupComplexElement(element as Partial<RadioButtonGroupComplexElement>); case 'drop-list': - return ElementFactory.createDropListElement(defaults); + return ElementFactory.createDropListElement(element as Partial<DropListElement>); case 'cloze': - return ElementFactory.createClozeElement(defaults); + return ElementFactory.createClozeElement(element as Partial<ClozeElement>); case 'slider': - return ElementFactory.createSliderElement(defaults); + return ElementFactory.createSliderElement(element as Partial<SliderElement>); case 'spell-correct': - return ElementFactory.createSpellCorrectElement(defaults); + return ElementFactory.createSpellCorrectElement(element as Partial<SpellCorrectElement>); case 'frame': - return ElementFactory.createFrameElement(defaults); + return ElementFactory.createFrameElement(element as Partial<FrameElement>); case 'toggle-button': - return ElementFactory.createToggleButtonElement(defaults); + return ElementFactory.createToggleButtonElement(element as Partial<ToggleButtonElement>); default: - throw new Error(`ElementType ${elementType} not found!`); + throw new Error(`ElementType ${element.type} not found!`); } } - static initElement(elementType: string, defaults: Record<string, number>): UIElement { + static initElement(element: Partial<UIElement>): UIElement { return { - type: elementType as UIElementType, - id: defaults.id ? String(defaults.id) : 'id_placeholder', - width: defaults.width || 190, - height: defaults.height || 60, - styles: {} + type: element.type as UIElementType, + id: element.id ? String(element.id) : 'id_placeholder', + width: element.width || 190, + height: element.height || 60 }; } - static initInputElement(elementType: string, defaults: Record<string, any>): InputElement { + static initInputElement(element: Partial<UIElement>): InputElement { return { - ...ElementFactory.initElement(elementType, defaults), - label: defaults.label || 'Beispielbeschriftung', - value: defaults.value || null, - required: defaults.required || false, - requiredWarnMessage: defaults.requiredWarnMessage || 'Eingabe erforderlich', - readOnly: defaults.readOnly || false + ...ElementFactory.initElement(element), + label: element.label as string || 'Beispielbeschriftung', + value: element.value !== undefined ? element.value as InputElementValue : null, + required: element.required !== undefined ? element.required as boolean : false, + requiredWarnMessage: element.requiredWarnMessage !== undefined ? + element.requiredWarnMessage as string : + 'Eingabe erforderlich', + readOnly: element.readOnly !== undefined ? element.readOnly as boolean : false }; } - static initPositionProps(defaults: Record<string, any> = {}): PositionProperties { + static initPositionProps(defaults: Record<string, UIElementValue> = {}): PositionProperties { return { - fixedSize: defaults.fixedSize || false, - dynamicPositioning: defaults.dynamicPositioning || false, - xPosition: defaults.xPosition || 0, - yPosition: defaults.yPosition || 0, - useMinHeight: defaults.useMinHeight || false, - gridColumnStart: defaults.gridColumnStart || 1, - gridColumnEnd: defaults.gridColumnEnd || 2, - gridRowStart: defaults.gridRowStart || 1, - gridRowEnd: defaults.gridRowEnd || 2, - marginLeft: defaults.marginLeft || 0, - marginRight: defaults.marginRight || 0, - marginTop: defaults.marginTop || 0, - marginBottom: defaults.marginBottom || 0, - zIndex: defaults.zIndex || 0 + fixedSize: defaults.fixedSize !== undefined ? defaults.fixedSize as boolean : false, + dynamicPositioning: defaults.dynamicPositioning !== undefined ? defaults.dynamicPositioning as boolean : false, + xPosition: defaults.xPosition !== undefined ? defaults.xPosition as number : 0, + yPosition: defaults.yPosition !== undefined ? defaults.yPosition as number : 0, + useMinHeight: defaults.useMinHeight !== undefined ? defaults.useMinHeight as boolean : false, + gridColumnStart: defaults.gridColumnStart !== undefined ? defaults.gridColumnStart as number : 1, + gridColumnEnd: defaults.gridColumnEnd !== undefined ? defaults.gridColumnEnd as number : 2, + gridRowStart: defaults.gridRowStart !== undefined ? defaults.gridRowStart as number : 1, + gridRowEnd: defaults.gridRowEnd !== undefined ? defaults.gridRowEnd as number : 2, + marginLeft: defaults.marginLeft !== undefined ? defaults.marginLeft as number : 0, + marginRight: defaults.marginRight !== undefined ? defaults.marginRight as number : 0, + marginTop: defaults.marginTop !== undefined ? defaults.marginTop as number : 0, + marginBottom: defaults.marginBottom !== undefined ? defaults.marginBottom as number : 0, + zIndex: defaults.zIndex !== undefined ? defaults.zIndex as number : 0 }; } - static initBasicStyles(defaults: Record<string, any>): BasicStyles { + static initBasicStyles(defaults: Record<string, UIElementValue> = {}): BasicStyles { return { - fontColor: defaults.fontColor || '#000000', - font: defaults.font || 'Roboto', - fontSize: defaults.fontSize || 20, - bold: defaults.bold || false, - italic: defaults.italic || false, - underline: defaults.underline || false, - backgroundColor: defaults.backgroundColor || '#d3d3d3' + fontColor: defaults.fontColor !== undefined ? defaults.fontColor as string : '#000000', + font: defaults.font !== undefined ? defaults.font as string : 'Roboto', + fontSize: defaults.fontSize !== undefined ? defaults.fontSize as number : 20, + bold: defaults.bold !== undefined ? defaults.bold as boolean : false, + italic: defaults.italic !== undefined ? defaults.italic as boolean : false, + underline: defaults.underline !== undefined ? defaults.underline as boolean : false, + backgroundColor: defaults.backgroundColor !== undefined ? defaults.backgroundColor as string : '#d3d3d3' }; } - static initPlayerProps(defaults: Record<string, any> = {}): PlayerProperties { + static initPlayerProps(defaults: Record<string, UIElementValue> = {}): PlayerProperties { return { - autostart: defaults.autostart || false, - autostartDelay: defaults.autostartDelay || 0, - loop: defaults.loop || false, - startControl: defaults.startControl || true, - pauseControl: defaults.pauseControl || false, - progressBar: defaults.progressBar || true, - interactiveProgressbar: defaults.interactiveProgressbar || undefined, - volumeControl: defaults.volumeControl || true, - defaultVolume: defaults.defaultVolume || 0.8, - minVolume: defaults.minVolume || 0, - muteControl: defaults.muteControl || true, - interactiveMuteControl: defaults.interactiveMuteControl || undefined, - hintLabel: defaults.hintLabel || '', - hintLabelDelay: defaults.hintLabelDelay || 0, - activeAfterID: defaults.activeAfterID || '', - minRuns: defaults.minRuns || 1, - maxRuns: defaults.maxRuns || null, - showRestRuns: defaults.showRestRuns || false, - showRestTime: defaults.showRestTime || true, - playbackTime: defaults.playbackTime || 0 + autostart: defaults.autostart !== undefined ? defaults.autostart as boolean : false, + autostartDelay: defaults.autostartDelay !== undefined ? defaults.autostartDelay as number : 0, + loop: defaults.loop !== undefined ? defaults.loop as boolean : false, + startControl: defaults.startControl !== undefined ? defaults.startControl as boolean : true, + pauseControl: defaults.pauseControl !== undefined ? defaults.pauseControl as boolean : false, + progressBar: defaults.progressBar !== undefined ? defaults.progressBar as boolean : true, + interactiveProgressbar: defaults.interactiveProgressbar !== undefined ? + defaults.interactiveProgressbar as boolean : + false, // TODO default? + volumeControl: defaults.volumeControl !== undefined ? defaults.volumeControl as boolean : true, + defaultVolume: defaults.defaultVolume !== undefined ? defaults.defaultVolume as number : 0.8, + minVolume: defaults.minVolume !== undefined ? defaults.minVolume as number : 0, + muteControl: defaults.muteControl !== undefined ? defaults.muteControl as boolean : true, + interactiveMuteControl: defaults.interactiveMuteControl !== undefined ? + defaults.interactiveMuteControl as boolean : + false, // TODO default? + hintLabel: defaults.hintLabel !== undefined ? defaults.hintLabel as string : '', + hintLabelDelay: defaults.hintLabelDelay !== undefined ? defaults.hintLabelDelay as number : 0, + activeAfterID: defaults.activeAfterID !== undefined ? defaults.activeAfterID as string : '', + minRuns: defaults.minRuns !== undefined ? defaults.minRuns as number : 1, + maxRuns: defaults.maxRuns !== undefined ? defaults.maxRuns as number | null : null, + showRestRuns: defaults.showRestRuns !== undefined ? defaults.showRestRuns as boolean : false, + showRestTime: defaults.showRestTime !== undefined ? defaults.showRestTime as boolean : true, + playbackTime: defaults.playbackTime !== undefined ? defaults.playbackTime as number : 0 }; } @@ -203,286 +209,302 @@ export abstract class ElementFactory { } } - private static createButtonElement(defaults: Record<string, any> = {}): ButtonElement { + private static createButtonElement(element: Partial<ButtonElement>): ButtonElement { return { - ...ElementFactory.initElement('button', defaults), + ...ElementFactory.initElement(element), type: 'button', - label: 'Knopf', - imageSrc: null, - action: null, - actionParam: null, - positionProps: ElementFactory.initPositionProps(defaults.positionProps), - styles: { - ...ElementFactory.initBasicStyles(defaults), - borderRadius: 0 + label: element.label !== undefined ? element.label : 'Knopf', + imageSrc: element.imageSrc || null, + action: element.action || null, + actionParam: element.actionParam || null, + position: ElementFactory.initPositionProps(element.positionProps as Record<string, UIElementValue>), + styling: { + ...ElementFactory.initBasicStyles(element.styling), + borderRadius: element.borderRadius !== undefined ? element.borderRadius as number : 0 } }; } - private static createCheckboxElement(defaults: Record<string, any> = {}): CheckboxElement { + private static createCheckboxElement(element: Partial<CheckboxElement>): CheckboxElement { return { - ...ElementFactory.initInputElement('checkbox', { width: 215, ...defaults }), + ...ElementFactory.initInputElement({ width: 215, ...element }), type: 'checkbox', - value: false, - positionProps: ElementFactory.initPositionProps(defaults), - styles: ElementFactory.initBasicStyles(defaults) + value: element.value !== undefined ? element.value : false, + position: ElementFactory.initPositionProps(element.position), + styling: ElementFactory.initBasicStyles(element.styling) }; } - private static createClozeElement(defaults: Record<string, any> = {}): ClozeElement { + private static createClozeElement(element: Partial<ClozeElement>): ClozeElement { return { - ...ElementFactory.initElement('cloze', { width: 450, height: 200, ...defaults }), + ...ElementFactory.initElement({ width: 450, height: 200, ...element }), type: 'cloze', - document: { type: 'doc', content: [] }, - positionProps: ElementFactory.initPositionProps(defaults), - styles: { - ...ElementFactory.initBasicStyles(defaults), - lineHeight: 135 + document: element.document !== undefined ? element.document : { type: 'doc', content: [] }, + position: ElementFactory.initPositionProps(element.position), + styling: { + ...ElementFactory.initBasicStyles(element.styling), + lineHeight: element.lineHeight !== undefined ? element.lineHeight as number : 135 } }; } - private static createDropdownElement(defaults: Record<string, any> = {}): DropdownElement { + private static createDropdownElement(element: Partial<DropdownElement>): DropdownElement { return { - ...ElementFactory.initInputElement('dropdown', { width: 240, height: 83, ...defaults }), + ...ElementFactory.initInputElement({ width: 240, height: 83, ...element }), type: 'dropdown', - options: [], - allowUnset: false, - positionProps: ElementFactory.initPositionProps(defaults), - styles: ElementFactory.initBasicStyles(defaults) + options: element.options !== undefined ? element.options : [], + allowUnset: element.allowUnset !== undefined ? element.allowUnset : false, + position: ElementFactory.initPositionProps(element), + styling: ElementFactory.initBasicStyles(element.styling) }; } - private static createDropListElement(defaults: Record<string, any> = {}): DropListElement { + private static createDropListElement(element: Partial<DropListElement>): DropListElement { return { - ...ElementFactory.initInputElement('drop-list', { height: 100, ...defaults }), + ...ElementFactory.initInputElement({ height: 100, ...element }), type: 'drop-list', - value: [], - onlyOneItem: false, - connectedTo: [], - orientation: 'vertical', - highlightReceivingDropList: false, - highlightReceivingDropListColor: '#006064', - positionProps: ElementFactory.initPositionProps({ useMinHeight: true, ...defaults }), - styles: { - ...ElementFactory.initBasicStyles({ backgroundColor: '#f4f4f2', ...defaults }), - itemBackgroundColor: '#c9e0e0' + value: element.value !== undefined ? element.value : [], + onlyOneItem: element.onlyOneItem !== undefined ? element.onlyOneItem : false, + connectedTo: element.connectedTo !== undefined ? element.connectedTo : [], + orientation: element.orientation !== undefined ? element.orientation : 'vertical', + highlightReceivingDropList: element.highlightReceivingDropList !== undefined ? + element.highlightReceivingDropList : + false, + highlightReceivingDropListColor: element.highlightReceivingDropListColor !== undefined ? + element.highlightReceivingDropListColor : '#006064', + position: ElementFactory.initPositionProps({ useMinHeight: true, ...element }), + styling: { + ...ElementFactory.initBasicStyles({ backgroundColor: '#f4f4f2', ...element.styling }), + itemBackgroundColor: element.itemBackgroundColor !== undefined ? + element.itemBackgroundColor as string : '#c9e0e0' } }; } - private static createDropListSimpleElement(defaults: Record<string, any> = {}): DropListSimpleElement { + private static createDropListSimpleElement(element: Partial<DropListSimpleElement>): DropListSimpleElement { return { - ...ElementFactory.initInputElement('drop-list-simple', { height: 100, ...defaults }), + ...ElementFactory.initInputElement({ height: 100, ...element }), type: 'drop-list', - value: [], - connectedTo: [], - highlightReceivingDropList: false, - highlightReceivingDropListColor: '#add8e6', - styles: { - ...ElementFactory.initBasicStyles({ backgroundColor: '#eeeeec', ...defaults }), - itemBackgroundColor: '#add8e6' + value: element.value !== undefined ? element.value : [], + connectedTo: element.connectedTo !== undefined ? element.connectedTo : [], + highlightReceivingDropList: element.highlightReceivingDropList !== undefined ? + element.highlightReceivingDropList : false, + highlightReceivingDropListColor: element.highlightReceivingDropListColor !== undefined ? + element.highlightReceivingDropListColor : '#add8e6', + styling: { + ...ElementFactory.initBasicStyles({ backgroundColor: '#eeeeec', ...element.styling }), + itemBackgroundColor: element.itemBackgroundColor !== undefined ? + element.itemBackgroundColor as string : '#add8e6' } }; } - private static createFrameElement(defaults: Record<string, any> = {}): FrameElement { + private static createFrameElement(element: Partial<FrameElement>): FrameElement { return { - ...ElementFactory.initElement('frame', {}), + ...ElementFactory.initElement({}), type: 'frame', - positionProps: ElementFactory.initPositionProps({ zIndex: -1, ...defaults }), - styles: { - ...ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...defaults }), - borderWidth: 1, - borderColor: 'black', - borderStyle: 'solid', - borderRadius: 0 + position: ElementFactory.initPositionProps({ zIndex: -1, ...element }), + styling: { + ...ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...element.styling }) as BasicStyles, + borderWidth: element.borderWidth !== undefined ? element.borderWidth as number : 1, + borderColor: element.borderColor !== undefined ? element.borderColor as string : 'black', + borderStyle: element.borderStyle !== undefined ? + element.borderStyle as 'solid' | 'dotted' | 'dashed' | 'double' | 'groove' | + 'ridge' | 'inset' | 'outset' : + 'solid', + borderRadius: element.borderRadius !== undefined ? element.borderRadius as number : 0 } }; } - private static createImageElement(defaults: Record<string, any> = {}): ImageElement { + private static createImageElement(element: Partial<ImageElement>): ImageElement { return { - ...ElementFactory.initElement('image', { height: 100, ...defaults }), + ...ElementFactory.initElement({ height: 100, ...element }), type: 'image', - src: defaults.src || '', // TODO eigentlich undefined - scale: false, - magnifier: false, - magnifierSize: 100, - magnifierZoom: 1.5, - magnifierUsed: false, - positionProps: ElementFactory.initPositionProps({}) + src: element.src || '', // TODO eigentlich undefined + scale: element.scale !== undefined ? element.scale : false, + magnifier: element.magnifier !== undefined ? element.magnifier : false, + magnifierSize: element.magnifierSize !== undefined ? element.magnifierSize : 100, + magnifierZoom: element.magnifierZoom !== undefined ? element.magnifierZoom : 1.5, + magnifierUsed: element.magnifierUsed !== undefined ? element.magnifierUsed : false, + position: ElementFactory.initPositionProps({}) }; } - private static createLikertElement(defaults: Record<string, any> = {}): LikertElement { + private static createLikertElement(element: Partial<LikertElement>): LikertElement { return { - ...ElementFactory.initElement('likert', { width: 250, height: 200, ...defaults }), + ...ElementFactory.initElement({ width: 250, height: 200, ...element }), type: 'likert', - rows: [], - columns: [], - firstColumnSizeRatio: 5, - readOnly: false, - positionProps: ElementFactory.initPositionProps({ marginBottom: 30, ...defaults }), - styles: { - ...ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...defaults }), - lineHeight: 135, - lineColoring: true, - lineColoringColor: '#c9e0e0' + rows: element.rows !== undefined ? element.rows : [], + columns: element.columns !== undefined ? element.columns : [], + firstColumnSizeRatio: element.firstColumnSizeRatio !== undefined ? element.firstColumnSizeRatio : 5, + readOnly: element.readOnly !== undefined ? element.readOnly : false, + position: ElementFactory.initPositionProps({ marginBottom: 30, ...element }), + styling: { + ...ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...element }), + lineHeight: element.lineHeight !== undefined ? element.lineHeight as number : 135, + lineColoring: element.lineColoring !== undefined ? element.lineColoring as boolean : true, + lineColoringColor: element.lineColoringColor !== undefined ? element.lineColoringColor as string : '#c9e0e0' } }; } - static createLikertRowElement(defaults: Record<string, any>): LikertRowElement { + static createLikertRowElement(element: Partial<LikertRowElement>): LikertRowElement { return { - ...ElementFactory.initInputElement('likert_row', defaults), + ...ElementFactory.initInputElement(element), type: 'likert-row', - text: defaults.text || '', - columnCount: defaults.columnCount || 0, - firstColumnSizeRatio: defaults.firstColumnSizeRatio || 5 + text: element.text !== undefined ? element.text : '', + columnCount: element.columnCount !== undefined ? element.columnCount : 0, + firstColumnSizeRatio: element.firstColumnSizeRatio !== undefined ? element.firstColumnSizeRatio : 5 }; } - private static createRadioButtonGroupElement(defaults: Record<string, any> = {}): RadioButtonGroupElement { + private static createRadioButtonGroupElement(element: Partial<RadioButtonGroupElement>): RadioButtonGroupElement { return { - ...ElementFactory.initInputElement('radio', { height: 85, ...defaults }), + ...ElementFactory.initInputElement({ height: 85, ...element }), type: 'radio', - options: [], - alignment: 'column', - strikeOtherOptions: false, - positionProps: ElementFactory.initPositionProps({ marginBottom: 30, ...defaults }), - styles: ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...defaults }) + options: element.options !== undefined ? element.options : [], + alignment: element.alignment !== undefined ? element.alignment : 'column', + strikeOtherOptions: element.strikeOtherOptions !== undefined ? element.strikeOtherOptions : false, + position: ElementFactory.initPositionProps({ marginBottom: 30, ...element.position }), + styling: ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...element.styling }) }; } - private static createRadioButtonGroupComplexElement(defaults: Record<string, any> = {}): RadioButtonGroupComplexElement { + private static createRadioButtonGroupComplexElement(element: Partial<RadioButtonGroupComplexElement>): + RadioButtonGroupComplexElement { return { - ...ElementFactory.initInputElement('radio-group-images', { height: 100, ...defaults }), // TODO better name + ...ElementFactory.initInputElement({ height: 100, ...element }), // TODO better name type: 'radio-group-images', - columns: [], - positionProps: ElementFactory.initPositionProps(defaults), - styles: ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...defaults }) + columns: element.columns !== undefined ? element.columns : [], + position: ElementFactory.initPositionProps(element.position), + styling: ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...element.styling }) }; } - private static createSliderElement(defaults: Record<string, any> = {}): SliderElement { + private static createSliderElement(element: Partial<SliderElement>): SliderElement { return { - ...ElementFactory.initInputElement('slider', { width: 300, height: 75, ...defaults }), + ...ElementFactory.initInputElement({ width: 300, height: 75, ...element }), type: 'slider', - minValue: 0, - maxValue: 100, - showValues: true, - barStyle: false, - thumbLabel: false, - positionProps: ElementFactory.initPositionProps(defaults), - styles: ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...defaults }) + minValue: element.minValue !== undefined ? element.minValue : 0, + maxValue: element.maxValue !== undefined ? element.maxValue : 100, + showValues: element.showValues !== undefined ? element.showValues : true, + barStyle: element.barStyle !== undefined ? element.barStyle : false, + thumbLabel: element.thumbLabel !== undefined ? element.thumbLabel : false, + position: ElementFactory.initPositionProps(element.position), + styling: ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...element.styling }) }; } - private static createSpellCorrectElement(defaults: Record<string, any> = {}): SpellCorrectElement { + private static createSpellCorrectElement(element: Partial<SpellCorrectElement>): SpellCorrectElement { return { - ...ElementFactory.initInputElement('spell-correct', { width: 230, height: 80, ...defaults }), + ...ElementFactory.initInputElement({ width: 230, height: 80, ...element }), type: 'spell-correct', - positionProps: ElementFactory.initPositionProps(defaults), - styles: ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...defaults }) + position: ElementFactory.initPositionProps(element.position), + styling: ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...element.styling }) }; } - private static createTextElement(defaults: Record<string, any> = {}): TextElement { + private static createTextElement(element: Partial<TextElement>): TextElement { return { - ...ElementFactory.initElement('text', { height: 98, ...defaults }), + ...ElementFactory.initElement({ height: 98, ...element }), type: 'text', - text: 'Lorem ipsum dolor sit amet', - highlightableOrange: false, - highlightableTurquoise: false, - highlightableYellow: false, - positionProps: ElementFactory.initPositionProps(defaults), - styles: { - ...ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...defaults }), - lineHeight: 135 + text: element.text !== undefined ? element.text : 'Lorem ipsum dolor sit amet', + highlightableOrange: element.highlightableOrange !== undefined ? element.highlightableOrange : false, + highlightableTurquoise: element.highlightableTurquoise !== undefined ? element.highlightableTurquoise : false, + highlightableYellow: element.highlightableYellow !== undefined ? element.highlightableYellow : false, + position: ElementFactory.initPositionProps(element.position), + styling: { + ...ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...element.styling }), + lineHeight: element.lineHeight !== undefined ? element.lineHeight as number : 135 } }; } - private static createTextAreaElement(defaults: Record<string, any> = {}): TextAreaElement { + private static createTextAreaElement(element: Partial<TextAreaElement>): TextAreaElement { return { - ...ElementFactory.initInputElement('text-area', { width: 230, height: 132, ...defaults }), + ...ElementFactory.initInputElement({ width: 230, height: 132, ...element }), type: 'text-area', - appearance: 'outline', - resizeEnabled: false, - rowCount: 3, - inputAssistancePreset: 'none', - inputAssistancePosition: 'floating', - positionProps: ElementFactory.initPositionProps(defaults), - styles: { - ...ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...defaults }), - lineHeight: 135 + appearance: element.appearance !== undefined ? element.appearance : 'outline', + resizeEnabled: element.resizeEnabled !== undefined ? element.resizeEnabled : false, + rowCount: element.rowCount !== undefined ? element.rowCount : 3, + inputAssistancePreset: element.inputAssistancePreset !== undefined ? element.inputAssistancePreset : 'none', + inputAssistancePosition: element.inputAssistancePosition !== undefined ? + element.inputAssistancePosition : 'floating', + position: ElementFactory.initPositionProps(element.position), + styling: { + ...ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...element.styling }), + lineHeight: element.lineHeight !== undefined ? element.lineHeight as number : 135 } }; } - private static createTextFieldElement(defaults: Record<string, any> = {}): TextFieldElement { + private static createTextFieldElement(element: Partial<TextFieldElement>): TextFieldElement { return { - ...ElementFactory.initInputElement('text-field', { width: 230, height: 100, ...defaults }), + ...ElementFactory.initInputElement({ width: 230, height: 100, ...element }), type: 'text-field', - appearance: 'outline', - minLength: 0, - minLengthWarnMessage: 'Eingabe zu kurz', - maxLength: 0, - maxLengthWarnMessage: 'Eingabe zu lang', - pattern: '', - patternWarnMessage: 'Eingabe entspricht nicht der Vorgabe', - inputAssistancePreset: 'none', - inputAssistancePosition: 'floating', - clearable: false, - positionProps: ElementFactory.initPositionProps(defaults), - styles: ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...defaults }) + appearance: element.appearance !== undefined ? element.appearance : 'outline', + minLength: element.minLength !== undefined ? element.minLength : 0, + minLengthWarnMessage: element.minLengthWarnMessage !== undefined ? + element.minLengthWarnMessage : 'Eingabe zu kurz', + maxLength: element.maxLength !== undefined ? element.maxLength : 0, + maxLengthWarnMessage: element.maxLengthWarnMessage !== undefined ? + element.maxLengthWarnMessage : 'Eingabe zu lang', + pattern: element.pattern !== undefined ? element.pattern : '', + patternWarnMessage: element.patternWarnMessage !== undefined ? + element.patternWarnMessage : 'Eingabe entspricht nicht der Vorgabe', + inputAssistancePreset: element.inputAssistancePreset !== undefined ? element.inputAssistancePreset : 'none', + inputAssistancePosition: element.inputAssistancePosition !== undefined ? + element.inputAssistancePosition : 'floating', + clearable: element.clearable !== undefined ? element.clearable : false, + position: ElementFactory.initPositionProps(element.position), + styling: ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...element.styling }) }; } - private static createTextFieldSimpleElement(defaults: Record<string, any> = {}): TextFieldSimpleElement { + private static createTextFieldSimpleElement(element: Partial<TextFieldSimpleElement>): TextFieldSimpleElement { return { - ...ElementFactory.initInputElement('text-field', { height: 25, ...defaults }), + ...ElementFactory.initInputElement({ height: 25, ...element }), type: 'text-field', - label: undefined, - styles: ElementFactory.initBasicStyles(defaults) + label: element.label !== undefined ? element.label : undefined, + styling: ElementFactory.initBasicStyles(element.styling) }; } - private static createToggleButtonElement(defaults: Record<string, any> = {}): ToggleButtonElement { + private static createToggleButtonElement(element: Partial<ToggleButtonElement>): ToggleButtonElement { return { - ...ElementFactory.initInputElement('toggle-button', { height: 25, ...defaults }), + ...ElementFactory.initInputElement({ height: 25, ...element }), type: 'toggle-button', - options: [], - strikeOtherOptions: false, - selectionColor: 'lightgreen', - verticalOrientation: false, - dynamicWidth: true, - styles: { - ...ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...defaults }), - lineHeight: 135 + options: element.options !== undefined ? element.options : [], + strikeOtherOptions: element.strikeOtherOptions !== undefined ? element.strikeOtherOptions : false, + selectionColor: element.selectionColor !== undefined ? element.selectionColor : 'lightgreen', + verticalOrientation: element.verticalOrientation !== undefined ? element.verticalOrientation : false, + dynamicWidth: element.dynamicWidth !== undefined ? element.dynamicWidth : true, + styling: { + ...ElementFactory.initBasicStyles({ backgroundColor: 'transparent', ...element }), + lineHeight: element.lineHeight !== undefined ? element.lineHeight as number : 135 } }; } - private static createAudioElement(defaults: Record<string, any> = {}): AudioElement { + private static createAudioElement(element: Partial<AudioElement>): AudioElement { return { - ...ElementFactory.initElement('audio', { width: 250, height: 90, ...defaults }), + ...ElementFactory.initElement({ width: 250, height: 90, ...element }), type: 'audio', - src: defaults.src || '', // TODO eigentlich undefined - positionProps: ElementFactory.initPositionProps(defaults), - playerProps: ElementFactory.initPlayerProps(defaults) + src: element.src !== undefined ? element.src : '', // TODO eigentlich undefined + position: ElementFactory.initPositionProps(element.position), + player: ElementFactory.initPlayerProps(element.player) }; } - private static createVideoElement(defaults: Record<string, any> = {}): VideoElement { + private static createVideoElement(element: Partial<VideoElement>): VideoElement { return { - ...ElementFactory.initElement('video', { width: 280, height: 230, ...defaults }), + ...ElementFactory.initElement({ width: 280, height: 230, ...element }), type: 'video', - src: defaults.src || '', // TODO eigentlich undefined - scale: false, - positionProps: ElementFactory.initPositionProps(defaults), - playerProps: ElementFactory.initPlayerProps(defaults) + src: element.src !== undefined ? element.src : '', // TODO eigentlich undefined + scale: element.scale !== undefined ? element.scale : false, + position: ElementFactory.initPositionProps(element.position), + player: ElementFactory.initPlayerProps(element.player) }; } } diff --git a/projects/common/util/unit-definition-sanitizer.ts b/projects/common/util/unit-definition-sanitizer.ts index 0dca30bcacd0ecb722a40094b5e4e8e4624e8203..84539163b7c102d7f0120aa5b5ba6ed1fe3a4162 100644 --- a/projects/common/util/unit-definition-sanitizer.ts +++ b/projects/common/util/unit-definition-sanitizer.ts @@ -1,199 +1,241 @@ import { Editor } from '@tiptap/core'; import StarterKit from '@tiptap/starter-kit'; -import { Page, Unit } from '../interfaces/unit'; +import { Page, Section, Unit } from '../interfaces/unit'; import { - DragNDropValueObject, DropListElement, PlayerElement, UIElement, UIElementValue + ClozeElement, + DragNDropValueObject, + DropListElement, + ElementStyling, InputElement, + PlayerProperties, PositionedElement, + PositionProperties, TextElement, + UIElement, + UIElementValue } from '../interfaces/elements'; +import { IdService } from '../../editor/src/app/services/id.service'; +import packageJSON from '../../../package.json'; +import { MessageService } from '../services/message.service'; import ToggleButtonExtension from '../tiptap-editor-extensions/toggle-button'; import DropListExtension from '../tiptap-editor-extensions/drop-list'; import TextFieldExtension from '../tiptap-editor-extensions/text-field'; -import { IdService } from '../../editor/src/app/services/id.service'; +import { ClozeDocument, ClozeDocumentParagraph, ClozeDocumentParagraphPart } from '../interfaces/cloze'; export abstract class UnitDefinitionSanitizer { - private static unitVersion: [number, number, number] = [0, 0, 0]; - - static campatibilityHandlers: { (s: UIElement[]): void; }[] = [ - UnitDefinitionSanitizer.handlePositionProps, - UnitDefinitionSanitizer.handleFontProps, - UnitDefinitionSanitizer.handleSurfaceProps, - UnitDefinitionSanitizer.handlePlayerProps, - UnitDefinitionSanitizer.handleTextElements, - UnitDefinitionSanitizer.handleClozeElements, - UnitDefinitionSanitizer.handleDropListElements, - UnitDefinitionSanitizer.handlePlusOne - ]; - - static sanitize(unitDefinition: Partial<Unit> & { pages: Page[], veronaModuleVersion?: string }): Unit { - UnitDefinitionSanitizer.unitVersion = unitDefinition.unitDefinitionType?.split('.') - .map(el => Number(el)) as [number, number, number] || - unitDefinition.veronaModuleVersion?.split('.') - .map(el => Number(el)) as [number, number, number]; - const elementList = UnitDefinitionSanitizer.getElementList(unitDefinition as Unit); - UnitDefinitionSanitizer.campatibilityHandlers.forEach(handler => handler(elementList)); - return unitDefinition as Unit; + private static unitVersion: [number, number, number] = + packageJSON.config.unit_definition_version.split('.') as unknown as [number, number, number]; + + static sanitizeUnitDefinition(unitDefinition: Unit & { veronaModuleVersion?: string }): Unit { + if (UnitDefinitionSanitizer.checkVersion(unitDefinition)) return unitDefinition; + + const x = { + ...unitDefinition, + pages: unitDefinition.pages.map((page: Page) => UnitDefinitionSanitizer.sanatizePage(page)) + }; + return x as Unit; } - private static getElementList(unitDefinition: Unit): UIElement[] { - return unitDefinition.pages.flat().map(page => page.sections.map(section => section.elements)).flat(2); + private static checkVersion(unitDefinition: Unit & { veronaModuleVersion?: string }) : boolean { + const defVersion: [number, number, number] = + unitDefinition.veronaModuleVersion?.split('@')[1].split('.') as unknown as [number, number, number]; + if (!UnitDefinitionSanitizer.isVersionOlderThanCurrent(defVersion)) { + return true; + } + MessageService.getInstance().showWarning('Loaded an outdated unit definition'); + return false; } - private static handlePositionProps(elementList: UIElement[]): void { - const positionProps = ['xPosition', 'yPosition', - 'useMinHeight', 'gridColumnStart', 'gridColumnEnd', 'gridRowStart', 'gridRowEnd', 'marginLeft', - 'marginRight', 'marginTop', 'marginBottom', 'zIndex', 'fixedSize', 'dynamicPositioning']; - UnitDefinitionSanitizer.movePropertiesToSubObject(elementList, 'positionProps', positionProps); + private static isVersionOlderThanCurrent(version: [number, number, number]): boolean { + if (version[0] < UnitDefinitionSanitizer.unitVersion[0]) { + return true; + } + if (version[1] < UnitDefinitionSanitizer.unitVersion[1]) { + return true; + } + return version[2] < UnitDefinitionSanitizer.unitVersion[2]; } - private static handleFontProps(elementList: UIElement[]): void { - const fontProps = ['fontColor', 'font', 'fontSize', 'lineHeight', 'bold', 'italic', 'underline']; - UnitDefinitionSanitizer.movePropertiesToSubObject(elementList, - 'styles', - fontProps, - 'fontProps'); + static sanatizePage(page: Page): Page { + return { + ...page, + sections: page.sections.map((section: Section) => UnitDefinitionSanitizer.sanatizeSection(section)) + }; } - private static handleSurfaceProps(elementList: UIElement[]): void { - UnitDefinitionSanitizer.movePropertiesToSubObject(elementList, - 'styles', - ['backgroundColor'], - 'surfaceProps'); + static sanatizeSection(section: Section): Section { + return { + ...section, + elements: section.elements.map((element: UIElement) => ( + UnitDefinitionSanitizer.sanatizeElement(element))) as PositionedElement[] + }; } - private static handlePlayerProps(elementList: UIElement[]): void { - const playerProps = ['autostart', 'autostartDelay', 'loop', 'startControl', 'pauseControl', - 'progressBar', 'interactiveProgressbar', 'volumeControl', 'defaultVolume', 'minVolume', - 'muteControl', 'interactiveMuteControl', 'hintLabel', 'hintLabelDelay', 'activeAfterID', - 'minRuns', 'maxRuns', 'showRestRuns', 'showRestTime', 'playbackTime']; - const filteredElementList: PlayerElement[] = - elementList.filter(element => ['audio', 'video'].includes(element.type)) as PlayerElement[]; - UnitDefinitionSanitizer.movePropertiesToSubObject(filteredElementList,'playerProps', playerProps); - filteredElementList.forEach((element: PlayerElement) => { - element.playerProps.defaultVolume = element.playerProps.defaultVolume || 0.8; - element.playerProps.minVolume = element.playerProps.minVolume || 0; - }); + static sanatizeElement(element: Record<string, UIElementValue>): UIElement { + let newElement: Partial<UIElement> = { + ...element, + position: UnitDefinitionSanitizer.getPositionProps(element), + styling: UnitDefinitionSanitizer.getStyleProps(element), + player: UnitDefinitionSanitizer.getPlayerProps(element) + }; + if (newElement.type === 'text') { + newElement = UnitDefinitionSanitizer.handleTextElement(newElement); + } + if (newElement.type === 'cloze') { + newElement = UnitDefinitionSanitizer.handleClozeElement(newElement as Record<string, UIElementValue>); + } + if (newElement.type === 'drop-list') { + newElement = UnitDefinitionSanitizer.handleDropListElement(newElement as Record<string, UIElementValue>); + } + if (['dropdown', 'radio', 'likert-row', 'radio-group-images', 'toggle-button'] + .includes(newElement.type as string)) { + newElement = UnitDefinitionSanitizer.handlePlusOne(newElement as InputElement); + } + return newElement as unknown as UIElement; } - private static movePropertiesToSubObject(elementList: UIElement[], - targetPropertyGroup: string, - propertyList: string[], - alternativeSourceGroup?: string): void { - elementList.forEach((element: UIElement) => { - let actualValues: Record<string, UIElementValue> = {}; - if (element[targetPropertyGroup]) { - actualValues = element[targetPropertyGroup] as Record<string, UIElementValue>; - } else if (alternativeSourceGroup && alternativeSourceGroup in element) { - actualValues = element[alternativeSourceGroup] as Record<string, UIElementValue>; - delete element[alternativeSourceGroup]; - } else { - actualValues = Object.keys(element) - .filter(key => propertyList.includes(key)) - .reduce((obj, key) => { - (obj as any)[key] = element[key]; - return obj; - }, {}); - } + private static getPositionProps(element: Record<string, UIElementValue>): PositionProperties { + if (element.position !== undefined) { + return element.position as PositionProperties; + } + if (element.positionProps !== undefined) { + return element.positionProps as PositionProperties; + } + return element as unknown as PositionProperties; + } - // delete old values - if (alternativeSourceGroup) delete element[alternativeSourceGroup]; - propertyList.forEach(prop => delete element[prop]); + /* Style properties are expected to be in 'stylings'. If not they may be in fontProps and/or + * surfaceProps. Even older versions had them in the root of the object, which is uses as last resort. + * The styles object then has all other properties of the element, but that is not a problem + * since the factory methods only use the values they care for and all others are discarded. */ + private static getStyleProps(element: Record<string, UIElementValue>): ElementStyling { + if (element.styling !== undefined) { + return element.styling as ElementStyling; + } + if (element.fontProps !== undefined) { + return { + ...(element.fontProps as Record<string, any>), + backgroundColor: (element.surfaceProps as Record<string, any>)?.backgroundColor, + borderRadius: element.borderRadius as number | undefined, + itemBackgroundColor: element.itemBackgroundColor as string | undefined, + borderWidth: element.borderWidth as number | undefined, + borderColor: element.borderColor as string | undefined, + borderStyle: element.borderStyle as + 'solid' | 'dotted' | 'dashed' | 'double' | 'groove' | 'ridge' | 'inset' | 'outset' | undefined, + lineColoring: element.lineColoring as boolean | undefined, + lineColoringColor: element.lineColoringColor as string | undefined + }; + } + return element as ElementStyling; + } - if (Object.keys(actualValues).length > 0) { - (element[targetPropertyGroup] as Record<string, UIElementValue>) = actualValues; - } - }); + private static getPlayerProps(element: Record<string, UIElementValue>): PlayerProperties { + if (element.playerProps !== undefined) { + return element.playerProps as PlayerProperties; + } + return element as unknown as PlayerProperties; } - private static handleTextElements(elementList: UIElement[]): void { - const textElements = elementList.filter(element => element.type === 'text'); - textElements.forEach((element: Record<string, unknown>) => { - if (element.highlightable || element.interaction === 'highlightable') { - element.highlightableYellow = true; - element.highlightableTurquoise = true; - element.highlightableOrange = true; - delete element.interaction; - delete element.highlightable; - } - if (element.interaction === 'underlinable') { - element.highlightableYellow = true; - delete element.interaction; - } - }); + private static handleTextElement(element: Record<string, UIElementValue>): TextElement { + const newElement = { ...element }; + if (newElement.highlightable || newElement.interaction === 'highlightable') { + newElement.highlightableYellow = true; + newElement.highlightableTurquoise = true; + newElement.highlightableOrange = true; + } + if (newElement.interaction === 'underlinable') { + newElement.highlightableYellow = true; + } + return newElement as TextElement; } /* - Replace raw text with backslash-markers with JSON representation. - The TipTap editor module can automate that. It needs plugins though to be able + Replace raw text with backslash-markers with HTML tags. + The TipTap editor module can create JSOM from the HTML. It needs plugins though to be able to create ui-elements. + Afterwards element models are added to the JSON. */ - private static handleClozeElements(elementList: UIElement[]): void { - const clozeElements = elementList.filter(element => element.type === 'cloze'); - if (clozeElements.length && clozeElements[0].text) { - clozeElements.forEach((element: Record<string, any>) => { - const replacedText = element.text.replace(/\\i|\\z|\\r/g, (match: string) => { - switch (match) { - case '\\i': - return '<aspect-nodeview-text-field></aspect-nodeview-text-field>'; - case '\\z': - return '<aspect-nodeview-drop-list></aspect-nodeview-drop-list>'; - case '\\r': - return '<aspect-nodeview-toggle-button></aspect-nodeview-toggle-button>'; - default: - throw Error('error in match'); - } - }); - const editor = new Editor({ - extensions: [ - StarterKit, - ToggleButtonExtension, - DropListExtension, - TextFieldExtension - ], - content: replacedText - }); - element.document = editor.getJSON(); - delete element.text; - }); - } - } + private static handleClozeElement(element: Record<string, UIElementValue>): ClozeElement { + if (!element.parts || !element.text) throw Error('Can\'t read Cloze Element'); + const uiElementParts = (element.parts as any[]) + .map((el: any) => el + .filter((el2: { type: string; }) => ['text-field', 'drop-list', 'toggle-button'].includes(el2.type))) + .flat(); - private static handleDropListElements(elementList: UIElement[]): void { - const dropListElements: DropListElement[] = - elementList.filter(element => element.type === 'drop-list') as DropListElement[]; - dropListElements.forEach((element: DropListElement) => { - if (element.options) { - element.value = []; - (element.options as string[]).forEach(option => { - (element.value as DragNDropValueObject[]).push({ - id: IdService.getInstance().getNewID('value'), - stringValue: option - }); - }); - delete element.options; - } - if (element.value && !((element.value as DragNDropValueObject[])[0] instanceof Object)) { - const newValues: DragNDropValueObject[] = []; - (element.value as string[]).forEach(value => { - newValues.push({ - id: IdService.getInstance().getNewID('value'), - stringValue: value - }); - }); - element.value = newValues; + const replacedText = (element.text as string).replace(/\\i|\\z|\\r/g, (match: string) => { + switch (match) { + case '\\i': + return '<aspect-nodeview-text-field></aspect-nodeview-text-field>'; + case '\\z': + return '<aspect-nodeview-drop-list></aspect-nodeview-drop-list>'; + case '\\r': + return '<aspect-nodeview-toggle-button></aspect-nodeview-toggle-button>'; + default: + throw Error('error in match'); } }); + + const editor = new Editor({ + extensions: [StarterKit, ToggleButtonExtension, DropListExtension, TextFieldExtension], + content: replacedText + }); + const doc: { type: string, content: ClozeDocumentParagraph[] } = + editor.getJSON() as { type: string, content: ClozeDocumentParagraph[] }; + + return { + ...element, + document: { + ...doc, + content: doc.content + .map((paragraph: ClozeDocumentParagraph) => ({ + ...paragraph, + content: paragraph.content + .map((paraPart: ClozeDocumentParagraphPart) => ( + ['TextField', 'DropList', 'ToggleButton'].includes(paraPart.type) ? + { + ...paraPart, + attrs: { + ...paraPart.attrs, + model: UnitDefinitionSanitizer.sanatizeElement(uiElementParts.shift().value) + } + } : + { + ...paraPart + } + )) + })) + } as ClozeDocument + } as ClozeElement; } - // version 1.1.0 is the only version where there was a plus one for values, which we rolled back afterwards - private static handlePlusOne(elementList: UIElement[]): void { - if (UnitDefinitionSanitizer.unitVersion === [1, 1, 0]) { - elementList.filter(el => ( - ['dropdown', 'radio', 'likert-row', 'radio-group-images', 'toggle-button'].includes(el.type) - )) - .forEach(element => { - if (element.value && element.value > 0) { - (element.value as number) -= 1; - } + private static handleDropListElement(element: Record<string, UIElementValue>): DropListElement { + const newElement = element; + if (newElement.options) { + newElement.value = []; + (newElement.options as string[]).forEach(option => { + (newElement.value as DragNDropValueObject[]).push({ + id: IdService.getInstance().getNewID('value'), + stringValue: option }); + }); } + if (newElement.value && !((newElement.value as DragNDropValueObject[])[0] instanceof Object)) { + const newValues: DragNDropValueObject[] = []; + (newElement.value as string[]).forEach(value => { + newValues.push({ + id: IdService.getInstance().getNewID('value'), + stringValue: value + }); + }); + newElement.value = newValues; + } + return newElement as DropListElement; + } + + // version 1.1.0 is the only version where there was a plus one for values, which was rolled back afterwards. + private static handlePlusOne(element: InputElement): InputElement { + return ((UnitDefinitionSanitizer.unitVersion === [1, 1, 0]) && (element.value && element.value > 0)) ? + { + ...element, + value: (element.value as number) - 1 + } : + element; } } diff --git a/projects/common/util/unit.factory.ts b/projects/common/util/unit.factory.ts index 626dbe56ad49ef7a7e925592aa1b2194b1a326de..d3b748d3af9d5e013e7c841a8f04e50f9471097b 100644 --- a/projects/common/util/unit.factory.ts +++ b/projects/common/util/unit.factory.ts @@ -1,36 +1,41 @@ import { Page, Section, Unit } from '../interfaces/unit'; +import { ElementFactory } from './element.factory'; +import { PositionedElement } from '../interfaces/elements'; +import packageJSON from '../../../package.json'; export abstract class UnitFactory { - static generateEmptyUnit(): Unit { + static createUnit(unit?: Unit): Unit { return { - unitDefinitionType: 'TODO', - pages: [UnitFactory.generateEmptyPage()] + unitDefinitionType: `veronaModuleVersion@${packageJSON.config.unit_definition_version}`, + pages: unit?.pages ? unit.pages.map(page => UnitFactory.createPage(page)) : [UnitFactory.createPage()] }; } - static generateEmptyPage(): Page { + static createPage(page?: Page): Page { return { - sections: [UnitFactory.generateEmptySection()], - hasMaxWidth: false, - maxWidth: 900, - margin: 30, + sections: page ? page.sections.map(section => UnitFactory.createSection(section)) : [UnitFactory.createSection()], + hasMaxWidth: page && page.hasMaxWidth !== undefined ? page.hasMaxWidth : false, + maxWidth: page && page.hasMaxWidth !== undefined ? page.maxWidth : 900, + margin: page && page.margin !== undefined ? page.margin : 30, backgroundColor: '#ffffff', - alwaysVisible: false, + alwaysVisible: page && page.alwaysVisible !== undefined ? page.alwaysVisible : false, alwaysVisiblePagePosition: 'left', - alwaysVisibleAspectRatio: 50 + alwaysVisibleAspectRatio: page && page.alwaysVisibleAspectRatio !== undefined ? page.alwaysVisibleAspectRatio : 50 }; } - static generateEmptySection(): Section { + static createSection(section?: Section): Section { return { - elements: [], - height: 400, + elements: section ? + section.elements.map(element => ElementFactory.createElement(element) as PositionedElement) : + [], + height: section && section.height !== undefined ? section.height : 400, backgroundColor: 'white', - dynamicPositioning: false, - autoColumnSize: true, - autoRowSize: true, - gridColumnSizes: '1fr 1fr', - gridRowSizes: '1fr' + dynamicPositioning: section && section.dynamicPositioning !== undefined ? section.dynamicPositioning : false, + autoColumnSize: section && section.autoColumnSize !== undefined ? section.autoColumnSize : true, + autoRowSize: section && section.autoRowSize !== undefined ? section.autoRowSize : true, + gridColumnSizes: section && section.gridColumnSizes !== undefined ? section.gridColumnSizes : '1fr 1fr', + gridRowSizes: section && section.gridRowSizes !== undefined ? section.gridRowSizes : '1fr' }; } } diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/canvas.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/canvas.component.ts index 4f8a0e7d93a097223abee08b40d2f7a76f0b5207..5f69ef8721f4acdb6b8820d9e7ba1602356a889f 100644 --- a/projects/editor/src/app/components/unit-view/page-view/canvas/canvas.component.ts +++ b/projects/editor/src/app/components/unit-view/page-view/canvas/canvas.component.ts @@ -80,7 +80,7 @@ export class CanvasComponent { event.container.data.sectionIndex); } else { selectedElements.forEach((element: PositionedElement) => { - let newXPosition = element.positionProps.xPosition + event.distance.x; + let newXPosition = element.position.xPosition + event.distance.x; if (newXPosition < 0) { newXPosition = 0; } @@ -89,7 +89,7 @@ export class CanvasComponent { } this.unitService.updateElementProperty([element], 'xPosition', newXPosition); - let newYPosition = element.positionProps.yPosition + event.distance.y; + let newYPosition = element.position.yPosition + event.distance.y; if (newYPosition < 0) { newYPosition = 0; } diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/overlays/dynamic-canvas-overlay.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/overlays/dynamic-canvas-overlay.component.ts index 36e3ce3c2a9ef5a048e3ca0fd35e0bcdd4ee1e86..881425cf6f5982ef4b463eafa6469fa0b0c070b6 100644 --- a/projects/editor/src/app/components/unit-view/page-view/canvas/overlays/dynamic-canvas-overlay.component.ts +++ b/projects/editor/src/app/components/unit-view/page-view/canvas/overlays/dynamic-canvas-overlay.component.ts @@ -8,47 +8,47 @@ import { UIElement } from '../../../../../../../../common/interfaces/elements'; @Component({ selector: 'aspect-dynamic-canvas-overlay', template: ` - <!-- TabIndex is needed to make the div selectable and catch keyboard events (delete). --> - <!-- DragStart and DragEnd are part of a cursor hack to style the body. See global styling file. --> - <div #draggableElement class="draggable-element" - [class.fixed-size-content-wrapper]="element.positionProps?.dynamicPositioning && - element.positionProps?.fixedSize" - [class.temporaryHighlight]="temporaryHighlight" - [style.display]="dragging ? 'none' : ''" - tabindex="-1" - cdkDrag [cdkDragData]="{dragType: 'move', element: element}" - (click)="selectElement($event.shiftKey); $event.stopPropagation()" (dblclick)="openEditDialog()" - (cdkDragStarted)="moveDragStart()" - (cdkDragEnded)="moveDragEnd()" - [style.outline]="isSelected ? 'purple solid 1px' : ''" - [style.z-index]="isSelected ? 2 : 1"> - <div *cdkDragPlaceholder></div> - <div *ngIf="isSelected" - [style.width.%]="dragging ? 100 : 0" - [style.height.%]="dragging ? 100 : 0" - cdkDrag [cdkDragData]="{dragType: 'resize', element: element}" - (cdkDragStarted)="resizeDragStart()" (cdkDragEnded)="resizeDragEnd()" - (cdkDragMoved)="resizeDragMove($event)"> - <div class="resizeHandle"> - <mat-icon>aspect_ratio</mat-icon> + <!-- TabIndex is needed to make the div selectable and catch keyboard events (delete). --> + <!-- DragStart and DragEnd are part of a cursor hack to style the body. See global styling file. --> + <div #draggableElement class="draggable-element" + [class.fixed-size-content-wrapper]="element.position?.dynamicPositioning && + element.position?.fixedSize" + [class.temporaryHighlight]="temporaryHighlight" + [style.display]="dragging ? 'none' : ''" + tabindex="-1" + cdkDrag [cdkDragData]="{dragType: 'move', element: element}" + (click)="selectElement($event.shiftKey); $event.stopPropagation()" (dblclick)="openEditDialog()" + (cdkDragStarted)="moveDragStart()" + (cdkDragEnded)="moveDragEnd()" + [style.outline]="isSelected ? 'purple solid 1px' : ''" + [style.z-index]="isSelected ? 2 : 1"> <div *cdkDragPlaceholder></div> - </div> - </div> - <div [class.fixed-size-content]="element.positionProps?.dynamicPositioning && - element.positionProps?.fixedSize" - [style.width]="element.positionProps?.dynamicPositioning && element.positionProps?.fixedSize ? + <div *ngIf="isSelected" + [style.width.%]="dragging ? 100 : 0" + [style.height.%]="dragging ? 100 : 0" + cdkDrag [cdkDragData]="{dragType: 'resize', element: element}" + (cdkDragStarted)="resizeDragStart()" (cdkDragEnded)="resizeDragEnd()" + (cdkDragMoved)="resizeDragMove($event)"> + <div class="resizeHandle"> + <mat-icon>aspect_ratio</mat-icon> + <div *cdkDragPlaceholder></div> + </div> + </div> + <div [class.fixed-size-content]="element.position?.dynamicPositioning && + element.position?.fixedSize" + [style.width]="element.position?.dynamicPositioning && element.position?.fixedSize ? element.width + 'px' : '100%'" - [style.height]="element.positionProps?.dynamicPositioning && element.positionProps?.fixedSize ? + [style.height]="element.position?.dynamicPositioning && element.position?.fixedSize ? element.height + 'px' : '100%'"> - <ng-template #elementContainer></ng-template> + <ng-template #elementContainer></ng-template> + </div> + </div> + <div class="resize-preview" + [style.position]="'relative'" + [style.width.px]="dragging ? elementWidth : 0" + [style.height.px]="dragging ? elementHeight : 0" + [style.border]="'1px dashed purple'"> </div> - </div> - <div class="resize-preview" - [style.position]="'relative'" - [style.width.px]="dragging ? elementWidth : 0" - [style.height.px]="dragging ? elementHeight : 0" - [style.border]="'1px dashed purple'"> - </div> `, styles: [ '.draggable-element {position: relative; width: 100%; height: 100%}', diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/overlays/static-canvas-overlay.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/overlays/static-canvas-overlay.component.ts index 35759f4082d2929963a0aa69fc810cfd087e9f06..a59f3fe1b72bf183995e23fcd3748743085182df 100644 --- a/projects/editor/src/app/components/unit-view/page-view/canvas/overlays/static-canvas-overlay.component.ts +++ b/projects/editor/src/app/components/unit-view/page-view/canvas/overlays/static-canvas-overlay.component.ts @@ -21,9 +21,9 @@ import { UIElement } from '../../../../../../../../common/interfaces/elements'; <!-- Needs extra div because styling can interfere with drag and drop--> <div [style.position]="'absolute'" [style.outline]="isSelected ? '1px dashed purple' : ''" - [style.left.px]="element.positionProps?.xPosition" - [style.top.px]="element.positionProps?.yPosition" - [style.z-index]="element.positionProps?.zIndex"> + [style.left.px]="element.position?.xPosition" + [style.top.px]="element.position?.yPosition" + [style.z-index]="element.position?.zIndex"> <div *ngIf="isSelected" class="resizeHandle" cdkDrag (cdkDragStarted)="resizeDragStart()" (cdkDragMoved)="resizeElement($event)" diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/section-dynamic.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/section-dynamic.component.ts index 9f4aa0865687e8b512f5944a461a256573a4282a..b0d93a45fe6c35ecaabc1922debf39db2c54594c 100644 --- a/projects/editor/src/app/components/unit-view/page-view/canvas/section-dynamic.component.ts +++ b/projects/editor/src/app/components/unit-view/page-view/canvas/section-dynamic.component.ts @@ -10,54 +10,54 @@ import { UIElement, UIElementType } from '../../../../../../../common/interfaces @Component({ selector: 'aspect-section-dynamic', template: ` - <div [style.display]="'grid'" - [style.grid-template-columns]="section.autoColumnSize ? '' : section.gridColumnSizes" - [style.grid-template-rows]="section.autoRowSize ? '' : section.gridRowSizes" - [style.height.%]="100" - cdkDropListGroup - [style.border]="isSelected ? '2px solid #ff4081': '1px dotted'" - [style.height.px]="section.height" - [style.background-color]="section.backgroundColor"> + <div [style.display]="'grid'" + [style.grid-template-columns]="section.autoColumnSize ? '' : section.gridColumnSizes" + [style.grid-template-rows]="section.autoRowSize ? '' : section.gridRowSizes" + [style.height.%]="100" + cdkDropListGroup + [style.border]="isSelected ? '2px solid #ff4081': '1px dotted'" + [style.height.px]="section.height" + [style.background-color]="section.backgroundColor"> - <!-- Dynamic sections have the droplists for the grid cells next to the actual elements. Elements can not - be children of the grid cells because they can span over multiple cells. --> - <ng-container *ngFor="let column of this.section.gridColumnSizes.split(' '); let x = index"> - <ng-container *ngFor="let row of this.section.gridRowSizes.split(' '); let y = index"> - <div class="grid-placeholder" - [style.grid-column-start]="x + 1" - [style.grid-column-end]="x + 1" - [style.grid-row-start]="y + 1" - [style.grid-row-end]="y + 1" - cdkDropList [cdkDropListData]="{ sectionIndex: sectionIndex, gridCoordinates: [x + 1, y + 1] }" - (cdkDropListDropped)="drop($any($event))" - id="list-{{sectionIndex}}-{{x+1}}-{{y+1}}" - (dragover)="$event.preventDefault()" (drop)="newElementDropped($event, x + 1, y + 1)"> - {{x + 1}} / {{y + 1}} - </div> - </ng-container> - </ng-container> + <!-- Dynamic sections have the droplists for the grid cells next to the actual elements. Elements can not + be children of the grid cells because they can span over multiple cells. --> + <ng-container *ngFor="let column of this.section.gridColumnSizes.split(' '); let x = index"> + <ng-container *ngFor="let row of this.section.gridRowSizes.split(' '); let y = index"> + <div class="grid-placeholder" + [style.grid-column-start]="x + 1" + [style.grid-column-end]="x + 1" + [style.grid-row-start]="y + 1" + [style.grid-row-end]="y + 1" + cdkDropList [cdkDropListData]="{ sectionIndex: sectionIndex, gridCoordinates: [x + 1, y + 1] }" + (cdkDropListDropped)="drop($any($event))" + id="list-{{sectionIndex}}-{{x+1}}-{{y+1}}" + (dragover)="$event.preventDefault()" (drop)="newElementDropped($event, x + 1, y + 1)"> + {{x + 1}} / {{y + 1}} + </div> + </ng-container> + </ng-container> - <aspect-dynamic-canvas-overlay *ngFor="let element of section.elements" - #elementComponent - [element]="$any(element)" - [style.min-width.px]="element.width" - [style.min-height.px]="element.height" - [style.margin-left.px]="element.positionProps.marginLeft" - [style.margin-right.px]="element.positionProps.marginRight" - [style.margin-top.px]="element.positionProps.marginTop" - [style.margin-bottom.px]="element.positionProps.marginBottom" - [style.grid-column-start]="element.positionProps.gridColumnStart" - [style.grid-column-end]="element.positionProps.gridColumnEnd" - [style.grid-row-start]="element.positionProps.gridRowStart" - [style.grid-row-end]="element.positionProps.gridRowEnd" - cdkDropList cdkDropListSortingDisabled - [cdkDropListData]="{ sectionIndex: sectionIndex }" - [cdkDropListConnectedTo]="dropListList" - (resize)="resizeOverlay($event)" - [style.position]="'relative'" - [style.pointer-events]="dragging ? 'none' : 'auto'"> - </aspect-dynamic-canvas-overlay> - </div> + <aspect-dynamic-canvas-overlay *ngFor="let element of section.elements" + #elementComponent + [element]="$any(element)" + [style.min-width.px]="element.width" + [style.min-height.px]="element.height" + [style.margin-left.px]="element.position.marginLeft" + [style.margin-right.px]="element.position.marginRight" + [style.margin-top.px]="element.position.marginTop" + [style.margin-bottom.px]="element.position.marginBottom" + [style.grid-column-start]="element.position.gridColumnStart" + [style.grid-column-end]="element.position.gridColumnEnd" + [style.grid-row-start]="element.position.gridRowStart" + [style.grid-row-end]="element.position.gridRowEnd" + cdkDropList cdkDropListSortingDisabled + [cdkDropListData]="{ sectionIndex: sectionIndex }" + [cdkDropListConnectedTo]="dropListList" + (resize)="resizeOverlay($event)" + [style.position]="'relative'" + [style.pointer-events]="dragging ? 'none' : 'auto'"> + </aspect-dynamic-canvas-overlay> + </div> `, styles: [ '.grid-placeholder {border: 5px solid aliceblue; color: lightblue; text-align: center;}' diff --git a/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-properties-panel.component.html b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-properties-panel.component.html index 142a03c6641abb2ee3dca3ad682fab5e642426e2..9441a47ae9db5c982e47aed211f9541dab285b40 100644 --- a/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-properties-panel.component.html +++ b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-properties-panel.component.html @@ -12,21 +12,21 @@ </aspect-element-model-properties-component> </mat-tab> - <mat-tab *ngIf="combinedProperties.positionProps"> + <mat-tab *ngIf="combinedProperties.position"> <ng-template mat-tab-label> <mat-icon class="example-tab-icon">format_shapes</mat-icon> </ng-template> <aspect-element-postion-properties [dimensions]="{ width: combinedProperties.width, height: combinedProperties.height }" - [positionProperties]="combinedProperties.positionProps" + [positionProperties]="combinedProperties.position" (updateModel)="updateModel($event.property, $event.value, $event.isInputValid)"> </aspect-element-postion-properties> </mat-tab> - <mat-tab *ngIf="combinedProperties.styles"> + <mat-tab *ngIf="combinedProperties.styling"> <ng-template mat-tab-label> <mat-icon class="example-tab-icon">palette</mat-icon> </ng-template> - <aspect-element-style-properties [styles]="$any(combinedProperties.styles)" + <aspect-element-style-properties [styles]="$any(combinedProperties.styling)" (updateModel)="updateModel($event.property, $event.value)"> </aspect-element-style-properties> </mat-tab> diff --git a/projects/editor/src/app/components/unit-view/page-view/properties-panel/model-properties-tab/element-model-properties.component.html b/projects/editor/src/app/components/unit-view/page-view/properties-panel/model-properties-tab/element-model-properties.component.html index 391edde53490b214ebfbf87ce8811a38879a8ca0..f20bbef13fb2d61141a20601d3c3db9c34b515bc 100644 --- a/projects/editor/src/app/components/unit-view/page-view/properties-panel/model-properties-tab/element-model-properties.component.html +++ b/projects/editor/src/app/components/unit-view/page-view/properties-panel/model-properties-tab/element-model-properties.component.html @@ -25,7 +25,7 @@ </button> </ng-container> - <ng-container *ngIf="combinedProperties.playerProps"> + <ng-container *ngIf="combinedProperties.player"> <button (click)="unitService.showDefaultEditDialog(selectedElements[0])"> <mat-icon>build_circle</mat-icon> </button> diff --git a/projects/editor/src/app/components/unit-view/page-view/properties-panel/style-properties-tab/element-style-properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties-panel/style-properties-tab/element-style-properties.component.ts index 4c6ea54972a4c705cd58d8bfcaa58847b5acf71f..7556ec391c8095e96c7fba8de8054a58c0c0649d 100644 --- a/projects/editor/src/app/components/unit-view/page-view/properties-panel/style-properties-tab/element-style-properties.component.ts +++ b/projects/editor/src/app/components/unit-view/page-view/properties-panel/style-properties-tab/element-style-properties.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { ElementStyles } from '../../../../../../../../common/interfaces/elements'; +import { ElementStyling } from '../../../../../../../../common/interfaces/elements'; @Component({ selector: 'aspect-element-style-properties', @@ -121,7 +121,7 @@ import { ElementStyles } from '../../../../../../../../common/interfaces/element ` }) export class ElementStylePropertiesComponent { - @Input() styles: ElementStyles = {}; + @Input() styles: ElementStyling = {}; @Output() updateModel = new EventEmitter<{ property: string; value: string | boolean, diff --git a/projects/editor/src/app/components/unit-view/unit-view.component.ts b/projects/editor/src/app/components/unit-view/unit-view.component.ts index e4229444f9bf5cdb0366df24aabd701b80d5c833..81c7d219cf2a8d4aab99eb70e7b5ac9471ad937b 100644 --- a/projects/editor/src/app/components/unit-view/unit-view.component.ts +++ b/projects/editor/src/app/components/unit-view/unit-view.component.ts @@ -32,7 +32,7 @@ export class UnitViewComponent implements OnDestroy { } addPage(): void { - this.unitService.unit.pages.push(UnitFactory.generateEmptyPage()); + this.unitService.unit.pages.push(UnitFactory.createPage()); this.selectedPageIndex = this.unitService.unit.pages.length - 1; this.selectionService.selectedPageIndex = this.selectedPageIndex; diff --git a/projects/editor/src/app/services/unit.service.ts b/projects/editor/src/app/services/unit.service.ts index 439f310694ee53a5f721ae8c2c5a154289ece9a2..8efbad2f27ca3dff82eec4da51af811c9235429b 100644 --- a/projects/editor/src/app/services/unit.service.ts +++ b/projects/editor/src/app/services/unit.service.ts @@ -42,17 +42,24 @@ export class UnitService { private dialogService: DialogService, private sanitizer: DomSanitizer, private translateService: TranslateService) { - this.unit = UnitFactory.generateEmptyUnit(); + this.unit = UnitFactory.createUnit({} as Unit); } loadUnitDefinition(unitDefinition: string): void { this.idService.reset(); - this.unit = UnitDefinitionSanitizer.sanitize(JSON.parse(unitDefinition)); + this.unit = UnitFactory.createUnit(UnitDefinitionSanitizer.sanitizeUnitDefinition(JSON.parse(unitDefinition))); UnitService.readIDs(this.unit); } private static readIDs(unit: Unit): void { UnitUtils.findUIElements(unit).forEach(element => { + if (element.type === 'likert') { + (element as LikertElement).rows.forEach(row => IdService.getInstance().addID(row.id)); + } + if (element.type === 'cloze') { + ClozeUtils.getClozeChildElements((element as ClozeElement).document) + .forEach(child => IdService.getInstance().addID(child.id)); + } IdService.getInstance().addID(element.id); }); } @@ -62,7 +69,7 @@ export class UnitService { } addSection(page: Page): void { - page.sections.push(UnitFactory.generateEmptySection()); + page.sections.push(UnitFactory.createSection()); this.veronaApiService.sendVoeDefinitionChangedNotification(); } @@ -112,29 +119,31 @@ export class UnitService { break; // no default } - newElement = ElementFactory.createElement(elementType, { // TODO + newElement = ElementFactory.createElement({ + type: elementType, id: this.idService.getNewID(elementType), src: mediaSrc, positionProps: { dynamicPositioning: section.dynamicPositioning } - } as unknown as Partial<UIElement>) as PositionedElement; + } as unknown as UIElement) as PositionedElement; } else { - newElement = ElementFactory.createElement(elementType, { + newElement = ElementFactory.createElement({ + type: elementType, id: this.idService.getNewID(elementType), positionProps: { dynamicPositioning: section.dynamicPositioning } - } as unknown as Partial<UIElement>) as PositionedElement; + } as unknown as UIElement) as PositionedElement; } if (coordinates && section.dynamicPositioning) { - newElement.positionProps.gridColumnStart = coordinates.x; - newElement.positionProps.gridColumnEnd = coordinates.x + 1; - newElement.positionProps.gridRowStart = coordinates.y; - newElement.positionProps.gridRowEnd = coordinates.y + 1; + newElement.position.gridColumnStart = coordinates.x; + newElement.position.gridColumnEnd = coordinates.x + 1; + newElement.position.gridRowStart = coordinates.y; + newElement.position.gridRowEnd = coordinates.y + 1; } else if (coordinates && !section.dynamicPositioning) { - newElement.positionProps.xPosition = coordinates.x; - newElement.positionProps.yPosition = coordinates.y; + newElement.position.xPosition = coordinates.x; + newElement.position.yPosition = coordinates.y; } section.elements.push(newElement); this.veronaApiService.sendVoeDefinitionChangedNotification(); @@ -164,7 +173,7 @@ export class UnitService { previousSection.elements = previousSection.elements.filter(element => !elements.includes(element)); elements.forEach(element => { newSection.elements.push(element as PositionedElement); - (element as PositionedElement).positionProps.dynamicPositioning = newSection.dynamicPositioning; + (element as PositionedElement).position.dynamicPositioning = newSection.dynamicPositioning; }); this.veronaApiService.sendVoeDefinitionChangedNotification(); } @@ -175,8 +184,8 @@ export class UnitService { (elements as PositionedElement[]).forEach((element: PositionedElement) => { const newElement = JSON.parse(JSON.stringify(element)); newElement.id = this.idService.getNewID(element.type); - newElement.positionProps.xPosition = element.positionProps.xPosition + 10; - newElement.positionProps.yPosition = element.positionProps.yPosition + 10; + newElement.positionProps.xPosition = element.position.xPosition + 10; + newElement.positionProps.yPosition = element.position.yPosition + 10; if ('value' in newElement && newElement.value instanceof Object) { // replace value Ids with fresh ones (dropList) newElement.value.forEach((valueObject: { id: string }) => { @@ -210,7 +219,7 @@ export class UnitService { if (property === 'dynamicPositioning') { section.dynamicPositioning = value as boolean; section.elements.forEach((element: PositionedElement) => { - element.positionProps.dynamicPositioning = value as boolean; + element.position.dynamicPositioning = value as boolean; }); } else { section[property] = value; @@ -248,16 +257,16 @@ export class UnitService { } else if (['fixedSize', 'dynamicPositioning', 'xPosition', 'yPosition', 'useMinHeight', 'gridColumnStart', 'gridColumnEnd', 'gridRowStart', 'gridRowEnd', 'marginLeft', 'marginRight', 'marginTop', 'marginBottom', 'zIndex'].includes(property)) { - element.positionProps![property] = Copy.getCopy(value); + element.position![property] = Copy.getCopy(value); } else if (['fontColor', 'font', 'fontSize', 'lineHeight', 'bold', 'italic', 'underline', 'backgroundColor', 'borderRadius', 'itemBackgroundColor', 'borderWidth', 'borderColor', 'borderStyle', 'lineColoring', 'lineColoringColor'].includes(property)) { - element.styles![property] = Copy.getCopy(value); + element.styling![property] = Copy.getCopy(value); } else if (['autostart', 'autostartDelay', 'loop', 'startControl', 'pauseControl', 'progressBar', 'interactiveProgressbar', 'volumeControl', 'defaultVolume', 'minVolume', 'muteControl', 'interactiveMuteControl', 'hintLabel', 'hintLabelDelay', 'activeAfterID', 'minRuns', 'maxRuns', 'showRestRuns', 'showRestTime', 'playbackTime'].includes(property)) { - element.playerProps![property] = Copy.getCopy(value); + element.player![property] = Copy.getCopy(value); } else { element[property] = Copy.getCopy(value); } @@ -358,28 +367,28 @@ export class UnitService { this.updateElementProperty( elements, 'xPosition', - Math.min(...elements.map(element => element.positionProps.xPosition)) + Math.min(...elements.map(element => element.position.xPosition)) ); break; case 'right': this.updateElementProperty( elements, 'xPosition', - Math.max(...elements.map(element => element.positionProps.xPosition)) + Math.max(...elements.map(element => element.position.xPosition)) ); break; case 'top': this.updateElementProperty( elements, 'yPosition', - Math.min(...elements.map(element => element.positionProps.yPosition)) + Math.min(...elements.map(element => element.position.yPosition)) ); break; case 'bottom': this.updateElementProperty( elements, 'yPosition', - Math.max(...elements.map(element => element.positionProps.yPosition)) + Math.max(...elements.map(element => element.position.yPosition)) ); break; // no default @@ -411,7 +420,7 @@ export class UnitService { case 'text': this.dialogService.showRichTextEditDialog( (element as TextElement).text, - (element as TextElement).styles.fontSize + (element as TextElement).styling.fontSize ).subscribe((result: string) => { if (result) { // TODO add proper sanitization @@ -426,7 +435,7 @@ export class UnitService { case 'cloze': this.dialogService.showClozeTextEditDialog( (element as ClozeElement).document, - (element as ClozeElement).styles.fontSize + (element as ClozeElement).styling.fontSize ).subscribe((result: string) => { if (result) { // TODO add proper sanitization @@ -456,7 +465,7 @@ export class UnitService { break; case 'audio': case 'video': - this.dialogService.showPlayerEditDialog((element as PlayerElement).playerProps) + this.dialogService.showPlayerEditDialog((element as PlayerElement).player) .subscribe((result: PlayerProperties) => { Object.keys(result).forEach(key => this.updateElementProperty([element], key, result[key])); }); diff --git a/projects/editor/src/app/text-editor/angular-node-views/drop-list-component-extension.ts b/projects/editor/src/app/text-editor/angular-node-views/drop-list-component-extension.ts index d2a909b8ffe9f2945ad1f0a909ecac65d978c8a6..4a762cf6d32034f8a029c67b45692cbc2e815f46 100644 --- a/projects/editor/src/app/text-editor/angular-node-views/drop-list-component-extension.ts +++ b/projects/editor/src/app/text-editor/angular-node-views/drop-list-component-extension.ts @@ -14,7 +14,7 @@ const DropListComponentExtension = (injector: Injector): Node => { addAttributes() { return { model: { - default: ElementFactory.createElement('drop-list', { height: 25, width: 100 }) + default: ElementFactory.createElement({ type: 'drop-list', height: 25, width: 100 }) } }; }, diff --git a/projects/editor/src/app/text-editor/angular-node-views/text-field-component-extension.ts b/projects/editor/src/app/text-editor/angular-node-views/text-field-component-extension.ts index 67d15a6ee6cc89f55cc1cf0e7bbfaff7679d2528..f3a8bda4133305be6c6ae55a057e72ba67d02964 100644 --- a/projects/editor/src/app/text-editor/angular-node-views/text-field-component-extension.ts +++ b/projects/editor/src/app/text-editor/angular-node-views/text-field-component-extension.ts @@ -13,7 +13,7 @@ const TextFieldComponentExtension = (injector: Injector): Node => { addAttributes() { return { model: { - default: ElementFactory.createElement('text-field-simple', { height: 25, width: 100 }) + default: ElementFactory.createElement({ type: 'text-field-simple', height: 25, width: 100 }) } }; }, diff --git a/projects/editor/src/app/text-editor/angular-node-views/toggle-button-component-extension.ts b/projects/editor/src/app/text-editor/angular-node-views/toggle-button-component-extension.ts index 57580092fec4d4a5230312edb1c81dde20a30fbe..b1f4da545aeed4d0270d3451dbe78a18a8454886 100644 --- a/projects/editor/src/app/text-editor/angular-node-views/toggle-button-component-extension.ts +++ b/projects/editor/src/app/text-editor/angular-node-views/toggle-button-component-extension.ts @@ -14,7 +14,7 @@ const ToggleButtonComponentExtension = (injector: Injector): Node => { addAttributes() { return { model: { - default: ElementFactory.createElement('toggle-button', { height: 25, width: 100 }) + default: ElementFactory.createElement({ type: 'toggle-button', height: 25, width: 100 }) } }; }, diff --git a/projects/editor/src/app/util/cloze-parser.ts b/projects/editor/src/app/util/cloze-parser.ts index 0ba9263b564912900fcb9242be466cbece5fe8b7..ec7694431ac15a02dbcec83e33495ee5746de135 100644 --- a/projects/editor/src/app/util/cloze-parser.ts +++ b/projects/editor/src/app/util/cloze-parser.ts @@ -38,13 +38,13 @@ export abstract class ClozeParser { let newElement: InputElement; switch (elementModel.type) { case 'text-field': - newElement = ElementFactory.createElement('text-field-simple', elementModel) as InputElement; + newElement = ElementFactory.createElement(elementModel as UIElement) as InputElement; break; case 'drop-list': - newElement = ElementFactory.createElement('drop-list', elementModel) as InputElement; + newElement = ElementFactory.createElement(elementModel as UIElement) as InputElement; break; case 'toggle-button': - newElement = ElementFactory.createElement('toggle-button', elementModel) as InputElement; + newElement = ElementFactory.createElement(elementModel as UIElement) as InputElement; break; default: throw new Error(`ElementType ${elementModel.type} not found!`); diff --git a/projects/editor/tsconfig.app.json b/projects/editor/tsconfig.app.json index b6ed01d7e8f7586b62929015f06a767a4d4a1dbe..fd37f74d77ffbd2f2c889c2b79b23e1332cc7c54 100644 --- a/projects/editor/tsconfig.app.json +++ b/projects/editor/tsconfig.app.json @@ -3,9 +3,7 @@ "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "../../out-tsc/app", - "types": [], - "resolveJsonModule": true, - "allowSyntheticDefaultImports": true + "types": [] }, "files": [ "src/main.ts", diff --git a/projects/player/src/app/app.component.ts b/projects/player/src/app/app.component.ts index 440cd9a7d51351c68c61088c24dc34d678ac099b..8ea7cadac94c0265f252d90d9e97aa6cb248ba11 100644 --- a/projects/player/src/app/app.component.ts +++ b/projects/player/src/app/app.component.ts @@ -13,6 +13,7 @@ import { MediaPlayerService } from './services/media-player.service'; import { Page, Unit } from '../../../common/interfaces/unit'; import { UnitDefinitionSanitizer } from '../../../common/util/unit-definition-sanitizer'; import { ValidatorService } from './services/validator.service'; +import { UnitFactory } from '../../../common/util/unit.factory'; @Component({ selector: 'aspect-player', @@ -60,7 +61,9 @@ export class AppComponent implements OnInit { // eslint-disable-next-line no-console console.log('player: onStart', message); if (message.unitDefinition) { - const unitDefinition: Unit = UnitDefinitionSanitizer.sanitize(JSON.parse(message.unitDefinition)); + const unitDefinition: Unit = UnitFactory.createUnit( + UnitDefinitionSanitizer.sanitizeUnitDefinition(JSON.parse(message.unitDefinition)) + ); this.unitStateElementMapperService.registerDropListValueIds(unitDefinition); this.playerConfig = message.playerConfig || {}; this.veronaPostService.sessionId = message.sessionId; diff --git a/projects/player/src/app/components/element-media-player-group/element-media-player-group.component.ts b/projects/player/src/app/components/element-media-player-group/element-media-player-group.component.ts index beb92a0dd627683f0c08d578745c7ed6c7bdecdf..4a26bacdfd9a3854fb42cde05a701f9f2b3d6cde 100644 --- a/projects/player/src/app/components/element-media-player-group/element-media-player-group.component.ts +++ b/projects/player/src/app/components/element-media-player-group/element-media-player-group.component.ts @@ -34,18 +34,18 @@ export class ElementMediaPlayerGroupComponent extends ElementGroupDirective impl ngOnInit(): void { const unitStateValue = this.unitStateService.getUnitStateElement(this.elementModel.id)?.value; this.initialValue = unitStateValue !== undefined ? - unitStateValue as number : (this.elementModel as AudioElement).playerProps.playbackTime; + unitStateValue as number : (this.elementModel as AudioElement).player.playbackTime; this.mediaPlayerService.registerMediaElement( this.elementModel.id, - this.elementModel.playerProps?.minRuns as number === 0 + this.elementModel.player?.minRuns as number === 0 ); } ngAfterViewInit(): void { const initialValue = this.elementModel.type === 'audio' ? - (this.elementModel as AudioElement).playerProps.playbackTime : - (this.elementModel as VideoElement).playerProps.playbackTime; + (this.elementModel as AudioElement).player.playbackTime : + (this.elementModel as VideoElement).player.playbackTime; this.registerAtUnitStateService(this.elementModel.id, initialValue, this.elementComponent, this.pageIndex); } } diff --git a/projects/player/src/app/components/element-splitter/element-splitter.component.html b/projects/player/src/app/components/element-splitter/element-splitter.component.html index 67fd2cd12077b29ebc21d55b1ad1dade515f3fb1..e55b688de31ae02b98335bc5812eeda3012aae2d 100644 --- a/projects/player/src/app/components/element-splitter/element-splitter.component.html +++ b/projects/player/src/app/components/element-splitter/element-splitter.component.html @@ -1,10 +1,10 @@ -<div [class]="elementModel.positionProps?.dynamicPositioning && elementModel.positionProps?.fixedSize ? +<div [class]="elementModel.position?.dynamicPositioning && elementModel.position?.fixedSize ? 'fixed-size-content-wrapper' : 'inline-container'"> - <div [class]="elementModel.positionProps?.dynamicPositioning && elementModel.positionProps?.fixedSize ? + <div [class]="elementModel.position?.dynamicPositioning && elementModel.position?.fixedSize ? 'fixed-size-content' : 'inline-container'" - [style.width]="elementModel.positionProps?.dynamicPositioning && elementModel.positionProps?.fixedSize ? + [style.width]="elementModel.position?.dynamicPositioning && elementModel.position?.fixedSize ? elementModel.width + 'px' : '100%'" - [style.height]="elementModel.positionProps?.dynamicPositioning && elementModel.positionProps?.fixedSize ? + [style.height]="elementModel.position?.dynamicPositioning && elementModel.position?.fixedSize ? elementModel.height + 'px' : '100%'"> <aspect-element-text-input-group *ngIf="'textInputGroup' === selectedGroup" @@ -43,4 +43,3 @@ </aspect-element-base-group> </div> </div> - diff --git a/projects/player/src/app/components/section/section.component.html b/projects/player/src/app/components/section/section.component.html index 282ee7602ac5047554893a3c2012e0f87fd471d3..305e835a21b7d714663b397905e0425ce6fb6b37 100644 --- a/projects/player/src/app/components/section/section.component.html +++ b/projects/player/src/app/components/section/section.component.html @@ -6,9 +6,9 @@ class="static-element fixed-size-content" [style.width.px]="element.width" [style.height.px]="element.height" - [style.left.px]="element.positionProps.xPosition" - [style.top.px]="element.positionProps.yPosition" - [style.z-index]="element.positionProps?.zIndex" + [style.left.px]="element.position.xPosition" + [style.top.px]="element.position.yPosition" + [style.z-index]="element.position?.zIndex" [elementModel]="element" [pageIndex]="pageIndex"> </aspect-element-splitter> @@ -26,16 +26,16 @@ <ng-container *ngFor="let element of section.elements; let i = index"> <aspect-element-splitter [style.min-width.px]="element.width" - [style.min-height.px]="element.positionProps.useMinHeight ? element.height : 0" - [style.margin-left.px]="element.positionProps.marginLeft" - [style.margin-right.px]="element.positionProps.marginRight" - [style.margin-top.px]="element.positionProps.marginTop" - [style.margin-bottom.px]="element.positionProps.marginBottom" - [style.grid-column-start]="element.positionProps.gridColumnStart" - [style.grid-column-end]="element.positionProps.gridColumnEnd" - [style.grid-row-start]="element.positionProps.gridRowStart" - [style.grid-row-end]="element.positionProps.gridRowEnd" - [style.z-index]="element.positionProps?.zIndex" + [style.min-height.px]="element.position.useMinHeight ? element.height : 0" + [style.margin-left.px]="element.position.marginLeft" + [style.margin-right.px]="element.position.marginRight" + [style.margin-top.px]="element.position.marginTop" + [style.margin-bottom.px]="element.position.marginBottom" + [style.grid-column-start]="element.position.gridColumnStart" + [style.grid-column-end]="element.position.gridColumnEnd" + [style.grid-row-start]="element.position.gridRowStart" + [style.grid-row-end]="element.position.gridRowEnd" + [style.z-index]="element.position?.zIndex" [elementModel]="element" [pageIndex]="pageIndex"> </aspect-element-splitter> diff --git a/projects/player/src/app/services/unit-state-element-mapper.service.ts b/projects/player/src/app/services/unit-state-element-mapper.service.ts index bc6a50f667f1e189e891bdd004ddf7fd1d37afd7..525637a6c9661ea8ef065965ab75b70d802f0e54 100644 --- a/projects/player/src/app/services/unit-state-element-mapper.service.ts +++ b/projects/player/src/app/services/unit-state-element-mapper.service.ts @@ -35,11 +35,11 @@ export class UnitStateElementMapperService { case 'audio': return unitStateValue !== undefined ? unitStateValue as number : - (elementModel as AudioElement).playerProps.playbackTime; + (elementModel as AudioElement).player.playbackTime; case 'video': return unitStateValue !== undefined ? unitStateValue as number : - (elementModel as VideoElement).playerProps.playbackTime; + (elementModel as VideoElement).player.playbackTime; case 'radio': case 'radio-group-images': case 'dropdown': diff --git a/tsconfig.json b/tsconfig.json index 874b1670a9453ed17359a8ca34d09929154f525b..e0901b6e51fe7c59a730b6c77acd69c0261bf4d3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,6 +14,8 @@ "experimentalDecorators": true, "moduleResolution": "node", "importHelpers": true, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, "target": "es2020", "module": "es2020", "lib": [