diff --git a/projects/common/models/uI-element.ts b/projects/common/models/uI-element.ts index 3b1e920165552e6ce96086a1f2ac9385c768247f..d9900867b06daa2a2ec25fe9562a02a94fe8479c 100644 --- a/projects/common/models/uI-element.ts +++ b/projects/common/models/uI-element.ts @@ -98,10 +98,19 @@ export interface ClozeDocumentParagraph { export interface ClozeDocumentPart { type: string; text?: string; - marks?: any; + marks?: any[]; attrs?: Record<string, string | number | boolean | InputElement>; } +export interface ClozeMarks { + 'font-weight'?: string; + 'font-style'?: string; + 'text-decoration'?: string; + fontSize?: string; + color?: string; + 'background-color'?: string; +} + export interface ValueChangeElement { id: string; values: [InputElementValue, InputElementValue]; diff --git a/projects/common/shared.module.ts b/projects/common/shared.module.ts index b7234be929b968d3cb9d77b09cdb803cb64cbc62..fd941d0b53467226931e2fb24ee2d336584f3bbc 100644 --- a/projects/common/shared.module.ts +++ b/projects/common/shared.module.ts @@ -50,7 +50,7 @@ import { DropListSimpleComponent } from './ui-elements/drop-list-simple/drop-lis import { FrameComponent } from './ui-elements/frame/frame.component'; import { ToggleButtonComponent } from './ui-elements/toggle-button/toggle-button.component'; import { MarkingBarComponent } from './components/marking-bar/marking-bar.component'; -import { MarkPipe } from './ui-elements/cloze/mark.pipe'; +import { StyleMarksPipe } from './ui-elements/cloze/styleMarks.pipe'; @NgModule({ imports: [ @@ -103,7 +103,7 @@ import { MarkPipe } from './ui-elements/cloze/mark.pipe'; FrameComponent, ToggleButtonComponent, MarkingBarComponent, - MarkPipe + StyleMarksPipe ], exports: [ CommonModule, diff --git a/projects/common/ui-elements/cloze/cloze.component.ts b/projects/common/ui-elements/cloze/cloze.component.ts index 436d1c99f3b7db287de30d8582d67e8247e98d0a..6269cedbb0a30b44666611bf8ae6f27280dd820b 100644 --- a/projects/common/ui-elements/cloze/cloze.component.ts +++ b/projects/common/ui-elements/cloze/cloze.component.ts @@ -17,24 +17,134 @@ import { FormElementComponent } from '../../directives/form-element-component.di [style.width]="elementModel.positionProps.fixedSize ? elementModel.width + 'px' : '100%'" [style.height]="elementModel.positionProps.fixedSize ? elementModel.height + 'px' : 'auto'"> <ng-container *ngFor="let part of elementModel.document.content"> - <p *ngIf="part.type === 'paragraph'" - [style.line-height.%]="elementModel.fontProps.lineHeight" - [style.color]="elementModel.fontProps.fontColor" - [style.font-family]="elementModel.fontProps.font" - [style.font-size.px]="elementModel.fontProps.fontSize" - [style.font-weight]="elementModel.fontProps.bold ? 'bold' : ''" - [style.font-style]="elementModel.fontProps.italic ? 'italic' : ''" - [style.text-decoration]="elementModel.fontProps.underline ? 'underline' : ''"> - <ng-container *ngFor="let subPart of part.content"> - <ng-container *ngIf="subPart.type === 'text'"> - <span [style.font-weight]="$any((subPart.marks | mark)?.includes('bold')) ? 'bold' : ''" - [style.font-style]="$any((subPart.marks | mark)?.includes('italic')) ? 'italic' : ''" - [style.text-decoration]="$any((subPart.marks | mark)?.includes('underline')) ? 'underline' : ''"> - {{subPart.text}} - </span> - </ng-container> - <span *ngIf="['ToggleButton', 'DropList', 'TextField'].includes(subPart.type)" - (click)="selectElement($any(subPart.attrs).model, $event)"> + <ul *ngIf="part.type === 'bulletList'"> + <li *ngFor="let listItem of part.content"> + <ng-container *ngFor="let listItemPart of $any(listItem).content" + [ngTemplateOutlet]="paragraphs" + [ngTemplateOutletContext]="{ $implicit: listItemPart }"></ng-container> + </li> + </ul> + <ol *ngIf="part.type === 'orderedList'"> + <li *ngFor="let listItem of part.content"> + <ng-container *ngFor="let listItemPart of $any(listItem).content" + [ngTemplateOutlet]="paragraphs" + [ngTemplateOutletContext]="{ $implicit: listItemPart }"></ng-container> + </li> + </ol> + <ng-container *ngIf="part.type !== 'bulletList'" + [ngTemplateOutlet]="paragraphs" + [ngTemplateOutletContext]="{ $implicit: part }"></ng-container> + </ng-container> + </div> + + <ng-template #paragraphs let-part> + <p *ngIf="part.type === 'paragraph'" + [style.line-height.%]="elementModel.fontProps.lineHeight" + [style.color]="elementModel.fontProps.fontColor" + [style.font-family]="elementModel.fontProps.font" + [style.font-size.px]="elementModel.fontProps.fontSize" + [style.font-weight]="elementModel.fontProps.bold ? 'bold' : ''" + [style.font-style]="elementModel.fontProps.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.fontProps.underline ? 'underline' : ''" + [style.margin-bottom]="part.attrs.margin + 'px'" + [style.margin-left]="part.attrs.hangingIndent ? '' : + ($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> + </p> + <h1 *ngIf="part.type === 'heading' && part.attrs.level === 1" + [style.display]="'inline'" + [style.line-height.%]="elementModel.fontProps.lineHeight" + [style.color]="elementModel.fontProps.fontColor" + [style.font-family]="elementModel.fontProps.font" + [style.font-size.px]="elementModel.fontProps.fontSize" + [style.font-weight]="elementModel.fontProps.bold ? 'bold' : ''" + [style.font-style]="elementModel.fontProps.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.fontProps.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.fontProps.lineHeight" + [style.color]="elementModel.fontProps.fontColor" + [style.font-family]="elementModel.fontProps.font" + [style.font-size.px]="elementModel.fontProps.fontSize" + [style.font-weight]="elementModel.fontProps.bold ? 'bold' : ''" + [style.font-style]="elementModel.fontProps.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.fontProps.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.fontProps.lineHeight" + [style.color]="elementModel.fontProps.fontColor" + [style.font-family]="elementModel.fontProps.font" + [style.font-size.px]="elementModel.fontProps.fontSize" + [style.font-weight]="elementModel.fontProps.bold ? 'bold' : ''" + [style.font-style]="elementModel.fontProps.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.fontProps.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.fontProps.lineHeight" + [style.color]="elementModel.fontProps.fontColor" + [style.font-family]="elementModel.fontProps.font" + [style.font-size.px]="elementModel.fontProps.fontSize" + [style.font-weight]="elementModel.fontProps.bold ? 'bold' : ''" + [style.font-style]="elementModel.fontProps.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.fontProps.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.fontProps.lineHeight" + [style.color]="elementModel.fontProps.fontColor" + [style.font-family]="elementModel.fontProps.font" + [style.font-size.px]="elementModel.fontProps.fontSize" + [style.font-weight]="elementModel.fontProps.bold ? 'bold' : ''" + [style.font-style]="elementModel.fontProps.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.fontProps.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.fontProps.lineHeight" + [style.color]="elementModel.fontProps.fontColor" + [style.font-family]="elementModel.fontProps.font" + [style.font-size.px]="elementModel.fontProps.fontSize" + [style.font-weight]="elementModel.fontProps.bold ? 'bold' : ''" + [style.font-style]="elementModel.fontProps.italic ? 'italic' : ''" + [style.text-decoration]="elementModel.fontProps.underline ? 'underline' : ''"> + <ng-container [ngTemplateOutlet]="paragraphChildren" + [ngTemplateOutletContext]="{ $implicit: part }"></ng-container> + </h6> + </ng-template> + + <ng-template #paragraphChildren let-part> + <ng-container *ngFor="let subPart of part.content"> + <ng-container *ngIf="$any(subPart).type === 'text'"> + <span [ngStyle]="subPart.marks | styleMarks"> + {{subPart.text}} + </span> + </ng-container> + <ng-container *ngIf="$any(subPart).type === 'image'"> + <img [src]="subPart.attrs.src" [alt]="subPart.attrs.alt" + [style.display]="'inline-block'" + [style.height]="'1em'" + [style.vertical-align]="'middle'"> + </ng-container> + <span *ngIf="['ToggleButton', 'DropList', 'TextField'].includes(subPart.type)" + (click)="selectElement($any(subPart.attrs).model, $event)"> <app-toggle-button *ngIf="subPart.type === 'ToggleButton'" #radioComponent [parentForm]="parentForm" [style.display]="'inline-block'" @@ -59,26 +169,8 @@ import { FormElementComponent } from '../../directives/form-element-component.di (elementValueChanged)="elementValueChanged.emit($event)"> </app-drop-list-simple> </span> - </ng-container> - </p> - <h1 *ngIf="part.type === 'heading' && part.attrs.level === 1" - [style.display]="'inline'"> - {{part.content[0].text}} - </h1> - <h2 *ngIf="part.type === 'heading' && part.attrs.level === 2" - [style.display]="'inline'"> - {{part.content[0].text}} - </h2> - <h3 *ngIf="part.type === 'heading' && part.attrs.level === 3" - [style.display]="'inline'"> - {{part.content[0].text}} - </h3> - <h4 *ngIf="part.type === 'heading' && part.attrs.level === 4" - [style.display]="'inline'"> - {{part.content[0].text}} - </h4> </ng-container> - </div> + </ng-template> `, styles: [ ':host ::ng-deep app-text-field {vertical-align: middle}', diff --git a/projects/common/ui-elements/cloze/mark.pipe.ts b/projects/common/ui-elements/cloze/mark.pipe.ts deleted file mode 100644 index d9b24d9e0a645f01f38bb62826c37b08f321b05b..0000000000000000000000000000000000000000 --- a/projects/common/ui-elements/cloze/mark.pipe.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Pipe, PipeTransform } from '@angular/core'; - -@Pipe({ - name: 'mark' -}) -/* This extracts marks from an item and puts them is a list to be searchable. - Only used in cloze component */ -export class MarkPipe implements PipeTransform { - transform(items: any[]): any[] { - if (!items) { - return items; - } - return items.map(item => item.type); - } -} diff --git a/projects/common/ui-elements/cloze/styleMarks.pipe.ts b/projects/common/ui-elements/cloze/styleMarks.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..7ce09a5cbcae7eb012aa26cb735c05276f198826 --- /dev/null +++ b/projects/common/ui-elements/cloze/styleMarks.pipe.ts @@ -0,0 +1,26 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { ClozeMarks } from '../../models/uI-element'; + +@Pipe({ + name: 'styleMarks' +}) +/* This extracts marks from a text item and puts them in an object to be consumed by ngStyle. + Only used in cloze component */ +export class StyleMarksPipe implements PipeTransform { + transform(items: any[] | undefined): ClozeMarks { + if (!items) { + return {}; + } + const markTypes = items.map(item => item.type); + const textStyles = items.filter(item => item.type === 'textStyle')[0]?.attrs; + const highlightAttributes = items.filter(item => item.type === 'highlight')[0]?.attrs; + return { + 'font-weight': markTypes.includes('bold') ? 'bold' : '', + 'font-style': markTypes.includes('italic') ? 'italic' : '', + 'text-decoration': markTypes.includes('underline') ? 'underline' : '', + fontSize: textStyles?.fontSize, + color: textStyles?.color, + 'background-color': highlightAttributes?.color + }; + } +}