From 96130fc5a3eed6e52277f826d72741589ebe23cd Mon Sep 17 00:00:00 2001 From: Alicia <Alicia.DeDiosFuente@unige.ch> Date: Tue, 17 May 2022 14:55:27 +0200 Subject: [PATCH 1/2] feat: [AoU-1183] add limit size for rich text area --- .../rich-text-editor.presentational.html | 6 ++++ .../rich-text-editor.presentational.ts | 34 ++++++++++++++++++- .../src/lib/models/label-translate.model.ts | 1 + 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.html b/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.html index 6490e0700..30ed94e52 100644 --- a/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.html +++ b/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.html @@ -16,6 +16,7 @@ [class.is-disabled]="formControl.disabled" (onFocus)="isFocused = true" (onBlur)="isFocused = false" + (onContentChanged)="preventLengthToExceed($event)" class="editor" ></quill-editor> <mat-error *ngIf="formValidationHelper.getFormError(formControl) as errors" @@ -24,3 +25,8 @@ <mat-hint *ngIf="withWordCounter | isTrue" class="word-counter" >{{numberWords}} {{labelTranslate.words | translate}}</mat-hint> + +<mat-hint *ngIf="withWordCounterAndMaxLength | isTrue" + class="word-counter" +>( {{this.numberCharactersWritten}} {{labelTranslate.characters | translate}} / {{this.maxLength}} + {{labelTranslate.characters | translate}} {{labelTranslate.maximum | translate}} )</mat-hint> diff --git a/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.ts b/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.ts index 664e01068..db4d29261 100644 --- a/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.ts +++ b/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.ts @@ -46,6 +46,7 @@ import { isNullOrUndefinedOrWhiteString, } from "../../../tools/is/is.tool"; import {AbstractPresentational} from "../abstract/abstract.presentational"; +import {ContentChange} from "ngx-quill"; @Component({ selector: "solidify-rich-text-editor", @@ -67,8 +68,17 @@ export class RichTextEditorPresentational extends AbstractPresentational impleme @Input() placeholder: string = ""; + private _formControl: FormControl; + @Input() - formControl: FormControl; + set formControl(formControl: FormControl) { + this._formControl = formControl; + this.numberCharactersWritten = formControl.value.length; + }; + + get formControl(): FormControl { + return this._formControl; + } @Input() modules: QuillModules = { @@ -86,6 +96,19 @@ export class RichTextEditorPresentational extends AbstractPresentational impleme @ViewChild("quillEditorComponent") quillEditorComponent: QuillEditorComponent; + + @Input() + maxLength: number | undefined = undefined; + + numberCharactersWritten: number | undefined; + + get withWordCounterAndMaxLength(): boolean { + if (this.maxLength !== undefined) { + return true; + } + return false; + } + isFocused: boolean = false; get numberWords(): number { @@ -104,6 +127,15 @@ export class RichTextEditorPresentational extends AbstractPresentational impleme return FormValidationHelper; } + preventLengthToExceed($event: ContentChange): void { + if (this.maxLength !== undefined) { + if ($event.editor.getLength() >= this.maxLength) { + $event.editor.deleteText(this.maxLength - 1, $event.editor.getLength()); + } + this.numberCharactersWritten = $event.editor.getLength(); + } + } + constructor(@Inject(LABEL_TRANSLATE) readonly labelTranslate: LabelTranslateInterface, private readonly _changeDetector: ChangeDetectorRef) { super(); diff --git a/projects/solidify-frontend/src/lib/models/label-translate.model.ts b/projects/solidify-frontend/src/lib/models/label-translate.model.ts index 26782c579..335f9be34 100644 --- a/projects/solidify-frontend/src/lib/models/label-translate.model.ts +++ b/projects/solidify-frontend/src/lib/models/label-translate.model.ts @@ -192,4 +192,5 @@ export interface LabelTranslateInterface { noCitationsAreAvailable: string; themeSelector: string; globalBannerClickForMoreInfo: string; + maximum: string; } -- GitLab From 02e704c1bbfe32915e6e68c99fa22a3fbbceeddc Mon Sep 17 00:00:00 2001 From: Florent Poittevin <florent.poittevin@unige.ch> Date: Mon, 23 May 2022 15:24:06 +0200 Subject: [PATCH 2/2] fix(rich text): prevent user to exceed limit of char and allow to display raw input --- .../rich-text-editor.presentational.html | 86 +++++++++++++------ .../rich-text-editor.presentational.scss | 4 + .../rich-text-editor.presentational.ts | 53 ++++++------ .../src/lib/models/label-translate.model.ts | 2 + 4 files changed, 93 insertions(+), 52 deletions(-) diff --git a/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.html b/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.html index 30ed94e52..7abf1d977 100644 --- a/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.html +++ b/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.html @@ -1,32 +1,70 @@ -<mat-label class="label" - [class.is-required]="formValidationHelper.hasRequiredField(formControl)" -><span class="text">{{label | translate}}</span> - <span *ngIf="formValidationHelper.hasRequiredField(formControl)" - aria-hidden="true" - class="mat-placeholder-required mat-form-field-required-marker" - > *</span> -</mat-label> +<ng-template [ngIf]="isInRichTextMode" + [ngIfElse]="rawTextMode" +> + <mat-label class="label" + [class.is-required]="formValidationHelper.hasRequiredField(formControl)" + ><span class="text">{{label | translate}}</span> + <span *ngIf="formValidationHelper.hasRequiredField(formControl)" + aria-hidden="true" + class="mat-placeholder-required mat-form-field-required-marker" + > *</span> + </mat-label> + <quill-editor #quillEditorComponent + [modules]="modules" + [formControl]="formControl" + [placeholder]="placeholder | translate" + [class.is-invalid]="formValidationHelper.displayInvalidWhenRequired(formControl, displayEmptyRequiredFieldInError)" + [class.is-focused]="isFocused" + [class.is-disabled]="formControl.disabled" + (onFocus)="isFocused = true" + (onBlur)="isFocused = false" + (onContentChanged)="preventLengthToExceed($event)" + class="editor" + ></quill-editor> +</ng-template> + +<ng-template #rawTextMode> + <mat-form-field [appearance]="appearanceInputMaterial" + [floatLabel]="positionLabelInputMaterial" + [class.mat-form-field-invalid]="(formValidationHelper.hasRequiredField(formControl) && (formControl.value | isNullOrUndefinedOrWhiteString)) || formControl.invalid" + class="mat-form-field" + solidifyTooltipOnEllipsis + > + <mat-label>{{label | translate}}</mat-label> + <textarea [formControl]="formControl" + [required]="formValidationHelper.hasRequiredField(formControl)" + [maxLength]="maxLength !== 0 ? maxLength : null" + cdkAutosizeMaxRows="10" + cdkAutosizeMinRows="5" + cdkTextareaAutosize + matInput + > + </textarea> + </mat-form-field> +</ng-template> -<quill-editor #quillEditorComponent - [modules]="modules" - [formControl]="formControl" - [placeholder]="placeholder | translate" - [class.is-invalid]="formValidationHelper.displayInvalidWhenRequired(formControl, displayEmptyRequiredFieldInError)" - [class.is-focused]="isFocused" - [class.is-disabled]="formControl.disabled" - (onFocus)="isFocused = true" - (onBlur)="isFocused = false" - (onContentChanged)="preventLengthToExceed($event)" - class="editor" -></quill-editor> <mat-error *ngIf="formValidationHelper.getFormError(formControl) as errors" class="errors" >{{errors}}</mat-error> + <mat-hint *ngIf="withWordCounter | isTrue" class="word-counter" ->{{numberWords}} {{labelTranslate.words | translate}}</mat-hint> +>{{numberWords}} {{labelTranslate.words | translate | lowercase}}</mat-hint> -<mat-hint *ngIf="withWordCounterAndMaxLength | isTrue" +<mat-hint *ngIf="withMaxLength | isTrue" class="word-counter" ->( {{this.numberCharactersWritten}} {{labelTranslate.characters | translate}} / {{this.maxLength}} - {{labelTranslate.characters | translate}} {{labelTranslate.maximum | translate}} )</mat-hint> +>( + <ng-template [ngIf]="isInRichTextMode | isFalse"> {{formControl.value.length}} {{labelTranslate.characters | + translate | lowercase}} / + </ng-template> + {{maxLength}} {{labelTranslate.characters | translate | lowercase}} {{labelTranslate.maximum | translate | lowercase}} ) +</mat-hint> + +<ng-template [ngIf]="formControl.disabled | isFalse"> + <a *ngIf="isInRichTextMode; else linkToggleToRichTextMode" + (click)="toggleTextMode()" + >{{labelTranslate.displayWithoutFormatting | translate}}</a> + <ng-template #linkToggleToRichTextMode> + <a (click)="toggleTextMode()">{{labelTranslate.displayWithFormatting | translate}}</a> + </ng-template> +</ng-template> diff --git a/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.scss b/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.scss index e8f29d320..f144c50ca 100644 --- a/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.scss +++ b/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.scss @@ -122,6 +122,10 @@ } } } + + .mat-form-field { + display: grid; + } } @include isInDarkMode { diff --git a/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.ts b/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.ts index db4d29261..0a8a9a1c9 100644 --- a/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.ts +++ b/projects/solidify-frontend/src/lib/components/presentationals/rich-text-editor/rich-text-editor.presentational.ts @@ -35,7 +35,10 @@ import { FormControl, NG_VALUE_ACCESSOR, } from "@angular/forms"; -import {QuillEditorComponent} from "ngx-quill"; +import { + ContentChange, + QuillEditorComponent, +} from "ngx-quill"; import {QuillModules} from "ngx-quill/lib/quill-editor.interfaces"; import {tap} from "rxjs/operators"; import {FormValidationHelper} from "../../../helpers/form-validation.helper"; @@ -44,9 +47,9 @@ import {LabelTranslateInterface} from "../../../models/label-translate.model"; import { isNonEmptyString, isNullOrUndefinedOrWhiteString, + isNumberReal, } from "../../../tools/is/is.tool"; import {AbstractPresentational} from "../abstract/abstract.presentational"; -import {ContentChange} from "ngx-quill"; @Component({ selector: "solidify-rich-text-editor", @@ -68,17 +71,8 @@ export class RichTextEditorPresentational extends AbstractPresentational impleme @Input() placeholder: string = ""; - private _formControl: FormControl; - @Input() - set formControl(formControl: FormControl) { - this._formControl = formControl; - this.numberCharactersWritten = formControl.value.length; - }; - - get formControl(): FormControl { - return this._formControl; - } + formControl: FormControl; @Input() modules: QuillModules = { @@ -100,17 +94,13 @@ export class RichTextEditorPresentational extends AbstractPresentational impleme @Input() maxLength: number | undefined = undefined; - numberCharactersWritten: number | undefined; + isFocused: boolean = false; + isInRichTextMode: boolean = true; - get withWordCounterAndMaxLength(): boolean { - if (this.maxLength !== undefined) { - return true; - } - return false; + get withMaxLength(): boolean { + return isNumberReal(this.maxLength); } - isFocused: boolean = false; - get numberWords(): number { const text = this.formControl.value as string; if (isNullOrUndefinedOrWhiteString(text)) { @@ -119,20 +109,18 @@ export class RichTextEditorPresentational extends AbstractPresentational impleme return text.split(" ").filter(term => isNonEmptyString(term)).length; } - get activeElement(): Element { - return document.activeElement; - } - get formValidationHelper(): typeof FormValidationHelper { return FormValidationHelper; } preventLengthToExceed($event: ContentChange): void { - if (this.maxLength !== undefined) { - if ($event.editor.getLength() >= this.maxLength) { - $event.editor.deleteText(this.maxLength - 1, $event.editor.getLength()); + if (this.withMaxLength) { + if ($event.html?.length > this.maxLength) { + const exceededCharLength = $event.html?.length - this.maxLength; + const textLength = $event.text?.length; + $event.editor.deleteText(textLength - exceededCharLength - 1, exceededCharLength); + this.formControl.setValue($event.editor.root.innerHTML); } - this.numberCharactersWritten = $event.editor.getLength(); } } @@ -151,6 +139,11 @@ export class RichTextEditorPresentational extends AbstractPresentational impleme this._changeDetector.detectChanges(); }), )); + this.subscribe(this.formControl.valueChanges.pipe( + tap(() => { + this._changeDetector.detectChanges(); + }), + )); } registerOnChange(fn: any): void { @@ -164,4 +157,8 @@ export class RichTextEditorPresentational extends AbstractPresentational impleme setDisabledState(isDisabled: boolean): void { } + + toggleTextMode(): void { + this.isInRichTextMode = !this.isInRichTextMode; + } } diff --git a/projects/solidify-frontend/src/lib/models/label-translate.model.ts b/projects/solidify-frontend/src/lib/models/label-translate.model.ts index 335f9be34..fa598b94e 100644 --- a/projects/solidify-frontend/src/lib/models/label-translate.model.ts +++ b/projects/solidify-frontend/src/lib/models/label-translate.model.ts @@ -193,4 +193,6 @@ export interface LabelTranslateInterface { themeSelector: string; globalBannerClickForMoreInfo: string; maximum: string; + displayWithFormatting: string; + displayWithoutFormatting: string; } -- GitLab