MOBILE-3749 rte: Add key actions for toolbar buttons

main
Pau Ferrer Ocaña 2021-05-10 17:50:56 +02:00
parent a41df38a71
commit 6127dac080
2 changed files with 84 additions and 28 deletions

View File

@ -16,98 +16,120 @@
<div #toolbar class="core-rte-toolbar" [class.toolbar-hidden]="toolbarHidden">
<button *ngIf="toolbarArrows" class="toolbar-arrow" [class.toolbar-arrow-hidden]="toolbarPrevHidden"
(click)="toolbarPrev($event)" (mousedown)="mouseDownAction($event)" [attr.aria-label]="'core.previous' | translate">
(click)="toolbarPrev($event)" (keyup)="toolbarPrev($event)"
(mousedown)="downAction($event)" (keydown)="downAction($event)"
[attr.aria-label]="'core.previous' | translate">
<ion-icon name="fas-chevron-left" aria-hidden="true"></ion-icon>
</button>
<ion-slides [options]="slidesOpts" [dir]="direction" (ionSlideDidChange)="updateToolbarArrows()">
<!-- https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand -->
<ion-slide>
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.strong" [title]="'core.editor.bold' | translate"
(click)="buttonAction($event, 'bold', 'strong')" (mousedown)="mouseDownAction($event)">
(click)="buttonAction($event, 'bold', 'strong')" (keyup)="buttonAction($event, 'bold', 'strong')"
(mousedown)="downAction($event)" (keydown)="downAction($event)">
<ion-icon name="fas-bold" aria-hidden="true"></ion-icon>
</button>
</ion-slide>
<ion-slide>
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.em" (click)="buttonAction($event, 'italic', 'em')"
(mousedown)="mouseDownAction($event)" [title]=" 'core.editor.italic' | translate">
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.em" [title]=" 'core.editor.italic' | translate"
(click)="buttonAction($event, 'italic', 'em')" (keyup)="buttonAction($event, 'italic', 'em')"
(mousedown)="downAction($event)" (keydown)="downAction($event)">
<ion-icon name="fas-italic" aria-hidden="true"></ion-icon>
</button>
</ion-slide>
<ion-slide>
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.u" (click)="buttonAction($event, 'underline', 'u')"
(mousedown)="mouseDownAction($event)" [title]="'core.editor.underline' | translate">
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.u" [title]="'core.editor.underline' | translate"
(click)="buttonAction($event, 'underline', 'u')" (keyup)="buttonAction($event, 'underline', 'u')"
(mousedown)="downAction($event)" (keydown)="downAction($event)"
>
<ion-icon name="fas-underline" aria-hidden="true"></ion-icon>
</button>
</ion-slide>
<ion-slide>
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.strike" [title]="'core.editor.strike' | translate"
(click)="buttonAction($event, 'strikethrough', 'strike')" (mousedown)="mouseDownAction($event)">
(click)="buttonAction($event, 'strikethrough', 'strike')" (keyup)="buttonAction($event, 'strikethrough', 'strike')"
(mousedown)="downAction($event)" (keydown)="downAction($event)">
<ion-icon name="fas-strikethrough" aria-hidden="true"></ion-icon>
</button>
</ion-slide>
<ion-slide>
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.p" (click)="buttonAction($event, 'p', 'block')"
(mousedown)="mouseDownAction($event)" [title]="'core.editor.p' | translate">
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.p" [title]="'core.editor.p' | translate"
(click)="buttonAction($event, 'p', 'block')" (keyup)="buttonAction($event, 'p', 'block')"
(mousedown)="downAction($event)" (keydown)="downAction($event)">
<ion-icon name="fas-paragraph" aria-hidden="true"></ion-icon>
</button>
</ion-slide>
<ion-slide>
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.h3" (click)="buttonAction($event, 'h3', 'block')"
(mousedown)="mouseDownAction($event)" [title]="'core.editor.h3' | translate">
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.h3" [title]="'core.editor.h3' | translate"
(click)="buttonAction($event, 'h3', 'block')" (keyup)="buttonAction($event, 'h3', 'block')"
(mousedown)="downAction($event)" (keydown)="downAction($event)" >
<ion-icon name="fas-heading" aria-hidden="true"></ion-icon><span aria-hidden="true">3</span>
</button>
</ion-slide>
<ion-slide>
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.h4" (click)="buttonAction($event, 'h4', 'block')"
(mousedown)="mouseDownAction($event)" [title]="'core.editor.h4' | translate">
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.h4" [title]="'core.editor.h4' | translate"
(click)="buttonAction($event, 'h4', 'block')" (keyup)="buttonAction($event, 'h4', 'block')"
(mousedown)="downAction($event)" (keydown)="downAction($event)">
<ion-icon name="fas-heading" aria-hidden="true"></ion-icon><span aria-hidden="true">4</span>
</button>
</ion-slide>
<ion-slide>
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.h5" (click)="buttonAction($event, 'h5', 'block')"
(mousedown)="mouseDownAction($event)" [title]="'core.editor.h5' | translate">
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.h5" [title]="'core.editor.h5' | translate"
(click)="buttonAction($event, 'h5', 'block')" (keyup)="buttonAction($event, 'h5', 'block')"
(mousedown)="downAction($event)" (keydown)="downAction($event)">
<ion-icon name="fas-heading" aria-hidden="true"></ion-icon><span aria-hidden="true">5</span>
</button>
</ion-slide>
<ion-slide>
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.ul" (mousedown)="mouseDownAction($event)"
(click)="buttonAction($event, 'insertUnorderedList')" [title]="'core.editor.unorderedlist' | translate">
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.ul"
[title]="'core.editor.unorderedlist' | translate"
(click)="buttonAction($event, 'insertUnorderedList')" (click)="buttonAction($event, 'insertUnorderedList')"
(mousedown)="downAction($event)" (keydown)="downAction($event)">
<ion-icon name="fas-list-ul" aria-hidden="true"></ion-icon>
</button>
</ion-slide>
<ion-slide>
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.ol" (mousedown)="mouseDownAction($event)"
(click)="buttonAction($event, 'insertOrderedList')" [title]="'core.editor.orderedlist' | translate">
<button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.ol" [title]="'core.editor.orderedlist' | translate"
(click)="buttonAction($event, 'insertOrderedList')" (keyup)="buttonAction($event, 'insertOrderedList')"
(mousedown)="downAction($event)" (keydown)="downAction($event)">
<ion-icon name="fas-list-ol" aria-hidden="true"></ion-icon>
</button>
</ion-slide>
<ion-slide>
<button [disabled]="!rteEnabled" (click)="buttonAction($event, 'removeFormat')" (mousedown)="mouseDownAction($event)"
<button [disabled]="!rteEnabled"
(click)="buttonAction($event, 'removeFormat')" (keyup)="buttonAction($event, 'removeFormat')"
(mousedown)="downAction($event)" (keydown)="downAction($event)"
[title]="'core.editor.clear' | translate">
<ion-icon name="fas-eraser" aria-hidden="true"></ion-icon>
</button>
</ion-slide>
<ion-slide *ngIf="canScanQR">
<button [disabled]="!rteEnabled" (click)="scanQR($event)" (mousedown)="stopBubble($event)"
<button [disabled]="!rteEnabled"
(click)="scanQR($event)" (keyup)="scanQR($event)"
(mousedown)="stopBubble($event)" (keydown)="stopBubble($event)"
[title]="'core.scanqr' | translate">
<ion-icon name="fas-qrcode" aria-hidden="true"></ion-icon>
</button>
</ion-slide>
<ion-slide>
<button [attr.aria-pressed]="!rteEnabled" (click)="toggleEditor($event)" (mousedown)="mouseDownAction($event)"
[title]=" 'core.editor.toggle' | translate">
<button [attr.aria-pressed]="!rteEnabled" [title]=" 'core.editor.toggle' | translate"
(click)="toggleEditor($event)" (keyup)="toggleEditor($event)"
(mousedown)="downAction($event)" (keydown)="downAction($event)">
<ion-icon name="fas-code" aria-hidden="true"></ion-icon>
</button>
</ion-slide>
<ion-slide *ngIf="isPhone">
<button (click)="hideToolbar($event)" (mousedown)="mouseDownAction($event)"
[title]=" 'core.editor.hidetoolbar' | translate">
<button [title]=" 'core.editor.hidetoolbar' | translate"
(click)="hideToolbar($event)" (keyup)="hideToolbar($event)"
(mousedown)="downAction($event)" (keydown)="downAction($event)">
<ion-icon name="fas-times" aria-hidden="true"></ion-icon>
</button>
</ion-slide>
</ion-slides>
<button *ngIf="toolbarArrows" class="toolbar-arrow" [class.toolbar-arrow-hidden]="toolbarNextHidden"
(click)="toolbarNext($event)" (mousedown)="mouseDownAction($event)" [attr.aria-label]="'core.next' | translate">
[attr.aria-label]="'core.next' | translate"
(click)="toolbarNext($event)" (keyup)="toolbarNext($event)"
(mousedown)="downAction($event)" (keydown)="downAction($event)" >
<ion-icon name="fas-chevron-right" aria-hidden="true"></ion-icon>
</button>
</div>

View File

@ -511,6 +511,10 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
* @param event The event.
*/
async toggleEditor(event: Event): Promise<void> {
if (event.type == 'keyup' && !this.isValidKeyboardKey(<KeyboardEvent>event)) {
return;
}
this.stopBubble(event);
this.setContent(this.control?.value || '');
@ -654,6 +658,10 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
* toolbar styles button when set.
*/
buttonAction(event: Event, command: string, parameters?: string): void {
if (event.type == 'keyup' && !this.isValidKeyboardKey(<KeyboardEvent>event)) {
return;
}
this.stopBubble(event);
if (!command) {
@ -725,6 +733,10 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
* Hide the toolbar in phone mode.
*/
hideToolbar(event: Event): void {
if (event.type == 'keyup' && !this.isValidKeyboardKey(<KeyboardEvent>event)) {
return;
}
this.element.classList.remove('has-focus');
this.stopBubble(event);
@ -734,6 +746,16 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
}
}
/**
* Checks if Space or Enter have been pressed.
*
* @param event Keyboard Event.
* @returns Wether space or enter have been pressed.
*/
protected isValidKeyboardKey(event: KeyboardEvent): boolean {
return event.key == ' ' || event.key == 'Enter';
}
/**
* Show the toolbar.
*/
@ -754,7 +776,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
* @param event Event.
*/
stopBubble(event: Event): void {
if (event.type != 'mouseup') {
if (event.type != 'mouseup' && event.type != 'keyup') {
event.preventDefault();
}
event.stopPropagation();
@ -765,7 +787,11 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
*
* @param event Event.
*/
mouseDownAction(event: Event): void {
downAction(event: Event): void {
if (event.type == 'keydown' && !this.isValidKeyboardKey(<KeyboardEvent>event)) {
return;
}
const selection = window.getSelection()?.toString();
// When RTE is focused with a whole paragraph in desktop the stopBubble will not fire click.
@ -778,6 +804,10 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
* Method that shows the next toolbar buttons.
*/
async toolbarNext(event: Event): Promise<void> {
if (event.type == 'keyup' && !this.isValidKeyboardKey(<KeyboardEvent>event)) {
return;
}
this.stopBubble(event);
if (!this.toolbarNextHidden) {
@ -792,6 +822,10 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
* Method that shows the previous toolbar buttons.
*/
async toolbarPrev(event: Event): Promise<void> {
if (event.type == 'keyup' && !this.isValidKeyboardKey(<KeyboardEvent>event)) {
return;
}
this.stopBubble(event);
if (!this.toolbarPrevHidden) {