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 8651ec386e1c9977b1805ee1c04df9938e323cf2..6cd951dbb46b051f2a6abe7504c312c7fbbddfe7 100644 --- a/projects/common/components/ui-elements/text-field-simple.component.ts +++ b/projects/common/components/ui-elements/text-field-simple.component.ts @@ -1,11 +1,13 @@ -import { Component, Input } from '@angular/core'; +import { + Component, EventEmitter, Input, Output +} from '@angular/core'; import { FormElementComponent } from '../../directives/form-element-component.directive'; import { TextFieldSimpleElement } from '../../interfaces/elements'; @Component({ selector: 'aspect-text-field-simple', template: ` - <input type="text" form="parentForm" + <input #input type="text" autocomplete="off" autocapitalize="none" autocorrect="off" @@ -21,7 +23,9 @@ import { TextFieldSimpleElement } from '../../interfaces/elements'; [style.text-decoration]="elementModel.styling.underline ? 'underline' : ''" [readonly]="elementModel.readOnly" [formControl]="elementFormControl" - [value]="elementModel.value"> + [value]="elementModel.value" + (focus)="elementModel.inputAssistancePreset !== 'none' ? onFocusChanged.emit(input) : null" + (blur)="elementModel.inputAssistancePreset !== 'none' ? onFocusChanged.emit(null): null"> `, styles: [ 'input {border: 1px solid rgba(0,0,0,.12); border-radius: 5px}' @@ -29,4 +33,5 @@ import { TextFieldSimpleElement } from '../../interfaces/elements'; }) export class TextFieldSimpleComponent extends FormElementComponent { @Input() elementModel!: TextFieldSimpleElement; + @Output() onFocusChanged = new EventEmitter<HTMLElement | null>(); } diff --git a/projects/common/directives/element-component.directive.ts b/projects/common/directives/element-component.directive.ts index 08d18bed0718ec643122fea65cdf34d08db03a90..0993eaf1161df45f933850dabe14799dde7dc8b2 100644 --- a/projects/common/directives/element-component.directive.ts +++ b/projects/common/directives/element-component.directive.ts @@ -9,7 +9,7 @@ export abstract class ElementComponent implements AfterContentChecked { abstract elementModel: UIElement; project!: 'player' | 'editor'; - constructor(private elementRef: ElementRef) {} + constructor(public elementRef: ElementRef) {} get domElement(): Element { return this.elementRef.nativeElement; diff --git a/projects/common/interfaces/elements.ts b/projects/common/interfaces/elements.ts index c5260db341ee740a2fef5e0267cc59380f319518..f706e3364e0dddf368cfe5a1b737253321626541 100644 --- a/projects/common/interfaces/elements.ts +++ b/projects/common/interfaces/elements.ts @@ -284,6 +284,8 @@ export interface TextAreaElement extends InputElement { export interface TextFieldSimpleElement extends InputElement { type: 'text-field'; + inputAssistancePreset: InputAssistancePreset; + inputAssistancePosition: 'floating' | 'right'; styling: BasicStyles; // TODO okay? bg-color? } diff --git a/projects/common/util/element.factory.ts b/projects/common/util/element.factory.ts index e220dd50129b6c8dbdb5f9134a99604959aab5be..762ca1cd8b1559402f29dc06e284a2fada7097a5 100644 --- a/projects/common/util/element.factory.ts +++ b/projects/common/util/element.factory.ts @@ -467,6 +467,9 @@ export abstract class ElementFactory { ...ElementFactory.initInputElement({ height: 25, ...element }), type: 'text-field', label: element.label !== undefined ? element.label : undefined, + inputAssistancePreset: element.inputAssistancePreset !== undefined ? element.inputAssistancePreset : 'none', + inputAssistancePosition: element.inputAssistancePosition !== undefined ? + element.inputAssistancePosition : 'floating', styling: ElementFactory.initBasicStyles(element.styling) }; } diff --git a/projects/player/src/app/components/element-compound-group/element-compound-group.component.html b/projects/player/src/app/components/element-compound-group/element-compound-group.component.html index 3431f87175e9e60d8f2373ae61e3b450520fd3d4..7c8b896eba999c320f466938c472e0ad594d271f 100644 --- a/projects/player/src/app/components/element-compound-group/element-compound-group.component.html +++ b/projects/player/src/app/components/element-compound-group/element-compound-group.component.html @@ -1,4 +1,5 @@ -<form [formGroup]="form"> +<form class="inline-container" + [formGroup]="form"> <aspect-cloze *ngIf="elementModel.type === 'cloze'" #elementComponent @@ -14,3 +15,15 @@ (childrenAdded)="onChildrenAdded($event)"> </aspect-likert> </form> + +<aspect-floating-keyboard + *ngIf="keyboardService.preset !== 'none'" + [isKeyboardOpen]="isKeyboardOpen && keyboardService.position === 'floating'" + [overlayOrigin]="keyboardService.elementComponent" + [inputElement]="keyboardService.inputElement" + [position]="keyboardService.position" + [preset]="keyboardService.preset" + [positionOffset]="0" + (deleteCharacter)="keyboardService.deleterCharacters()" + (enterKey)="keyboardService.enterKey($event)"> +</aspect-floating-keyboard> diff --git a/projects/player/src/app/components/element-compound-group/element-compound-group.component.ts b/projects/player/src/app/components/element-compound-group/element-compound-group.component.ts index b5f07265c914c7654565115b2f305345d4480777..8a24bbb027ceab5b51a5e4600231ccfd29de1ca8 100644 --- a/projects/player/src/app/components/element-compound-group/element-compound-group.component.ts +++ b/projects/player/src/app/components/element-compound-group/element-compound-group.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, ViewChild } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { - ClozeElement, InputElement, LikertElement + ClozeElement, InputElement, LikertElement, TextFieldElement } from '../../../../../common/interfaces/elements'; import { ClozeUtils } from '../../../../../common/util/cloze'; import { UnitStateService } from '../../services/unit-state.service'; @@ -11,6 +11,10 @@ import { ElementFormGroupDirective } from '../../directives/element-form-group.d import { MessageService } from '../../../../../common/services/message.service'; import { VeronaSubscriptionService } from '../../services/verona-subscription.service'; import { ValidatorService } from '../../services/validator.service'; +import { KeyboardService } from '../../services/keyboard.service'; +import { TextAreaComponent } from '../../../../../common/components/ui-elements/text-area.component'; +import { TextFieldComponent } from '../../../../../common/components/ui-elements/text-field.component'; +import { TextFieldSimpleComponent } from '../../../../../common/components/ui-elements/text-field-simple.component'; @Component({ selector: 'aspect-element-compound-group', @@ -19,10 +23,12 @@ import { ValidatorService } from '../../services/validator.service'; }) export class ElementCompoundGroupComponent extends ElementFormGroupDirective implements OnInit { @ViewChild('elementComponent') elementComponent!: ElementComponent; + isKeyboardOpen!: boolean; ClozeElement!: ClozeElement; LikertElement!: LikertElement; constructor( + public keyboardService: KeyboardService, public unitStateService: UnitStateService, public unitStateElementMapperService: UnitStateElementMapperService, public translateService: TranslateService, @@ -44,6 +50,24 @@ export class ElementCompoundGroupComponent extends ElementFormGroupDirective imp children.forEach(child => { const childModel = child.elementModel as InputElement; this.registerAtUnitStateService(childModel.id, childModel.value, child, this.pageIndex); + if (childModel.type === 'text-field') { + (child as TextFieldSimpleComponent) + .onFocusChanged.subscribe(element => this.onFocusChanged(element, child as TextFieldComponent)); + } }); } + + onFocusChanged(focussedElement: HTMLElement | null, elementComponent: TextAreaComponent | TextFieldComponent): void { + if (focussedElement) { + const focussedInputElement = this.elementModel.type === 'text-area' ? + focussedElement as HTMLTextAreaElement : + focussedElement as HTMLInputElement; + const preset = (elementComponent.elementModel as TextFieldElement).inputAssistancePreset; + const position = (elementComponent.elementModel as TextFieldElement).inputAssistancePosition; + this.isKeyboardOpen = this.keyboardService + .openKeyboard(focussedInputElement, preset, position, elementComponent); + } else { + this.isKeyboardOpen = this.keyboardService.closeKeyboard(); + } + } } diff --git a/projects/player/src/app/components/element-text-input-group/element-text-input-group.component.html b/projects/player/src/app/components/element-text-input-group/element-text-input-group.component.html index 8e451a2b56e06340bd5b53e88ba94e425c5fd2e9..f79fb974fdc6e56344889a819e61216d78a90b74 100644 --- a/projects/player/src/app/components/element-text-input-group/element-text-input-group.component.html +++ b/projects/player/src/app/components/element-text-input-group/element-text-input-group.component.html @@ -1,27 +1,25 @@ -<div class="inline-container" cdkOverlayOrigin #overlayOrigin="cdkOverlayOrigin"> - <form [formGroup]="form"> - <aspect-text-area - *ngIf="elementModel.type === 'text-area'" - #elementComponent - [parentForm]="form" - [elementModel]="elementModel | cast: TextAreaElement" - (onFocusChanged)="onFocusChanged($event, elementComponent)"> - </aspect-text-area> - <aspect-text-field - *ngIf="elementModel.type === 'text-field'" - #elementComponent - [parentForm]="form" - [elementModel]="elementModel | cast: TextFieldElement" - (onFocusChanged)="onFocusChanged($event, elementComponent)"> - </aspect-text-field> - </form> -</div> +<form class="inline-container" + [formGroup]="form"> + <aspect-text-area + *ngIf="elementModel.type === 'text-area'" + #elementComponent + [parentForm]="form" + [elementModel]="elementModel | cast: TextAreaElement" + (onFocusChanged)="onFocusChanged($event, elementComponent)"> + </aspect-text-area> + <aspect-text-field + *ngIf="elementModel.type === 'text-field'" + #elementComponent + [parentForm]="form" + [elementModel]="elementModel | cast: TextFieldElement" + (onFocusChanged)="onFocusChanged($event, elementComponent)"> + </aspect-text-field> +</form> <aspect-floating-keyboard - *ngIf="keyboardService.preset !== 'none' && - (elementModel.type === 'text-area' || elementModel.type === 'text-field')" + *ngIf="keyboardService.preset !== 'none'" [isKeyboardOpen]="isKeyboardOpen && keyboardService.position === 'floating'" - [overlayOrigin]="overlayOrigin" + [overlayOrigin]="elementComponent" [inputElement]="keyboardService.inputElement" [position]="keyboardService.position" [preset]="keyboardService.preset"