From 89cca2478ccd1fe2fcd67c7adddd3e7961e19121 Mon Sep 17 00:00:00 2001 From: Sibiraj <20282546+sibiraj-s@users.noreply.github.com> Date: Tue, 14 May 2024 08:07:29 +0530 Subject: [PATCH] fix: regression with focus on keyboard menu interaction --- .../modules/menu/bubble/bubble.component.html | 2 +- .../color-picker/color-picker.component.html | 16 +++++-- .../color-picker/color-picker.component.ts | 47 +++++++++++++++---- .../menu/dropdown/dropdown.component.html | 8 +++- .../menu/dropdown/dropdown.component.ts | 39 +++++++++++---- .../modules/menu/image/image.component.html | 4 +- .../lib/modules/menu/image/image.component.ts | 18 +++++-- .../insert-command.component.html | 4 +- .../insert-command.component.ts | 10 +++- .../lib/modules/menu/link/link.component.html | 4 +- .../lib/modules/menu/link/link.component.ts | 24 ++++++---- .../toggle-command.component.html | 4 +- .../toggle-command.component.ts | 16 +++++-- 13 files changed, 151 insertions(+), 45 deletions(-) diff --git a/projects/ngx-editor/src/lib/modules/menu/bubble/bubble.component.html b/projects/ngx-editor/src/lib/modules/menu/bubble/bubble.component.html index 3b822616..1628cb1c 100644 --- a/projects/ngx-editor/src/lib/modules/menu/bubble/bubble.component.html +++ b/projects/ngx-editor/src/lib/modules/menu/bubble/bubble.component.html @@ -8,7 +8,7 @@ 'NgxBubbleMenu__Icon--Active': this.activeItems.includes(item), 'NgxEditor--Disabled': !this.execulableItems.includes(item) }" - (click)="onClick($event, item)" + (mousedown)="onClick($event, item)" [title]="getTitle(item) | async" [innerHTML]="getIcon(item)" > diff --git a/projects/ngx-editor/src/lib/modules/menu/color-picker/color-picker.component.html b/projects/ngx-editor/src/lib/modules/menu/color-picker/color-picker.component.html index 44c14e72..fe1c3d7e 100644 --- a/projects/ngx-editor/src/lib/modules/menu/color-picker/color-picker.component.html +++ b/projects/ngx-editor/src/lib/modules/menu/color-picker/color-picker.component.html @@ -5,7 +5,9 @@ [class.NgxEditor--Disabled]="!canExecute" [disabled]="!canExecute" [innerHTML]="icon | sanitizeHtml" - (click)="togglePopup($event)" + (mousedown)="onTogglePopupMouseClick($event)" + (keydown.enter)="onTogglePopupKeydown()" + (keydown.space)="onTogglePopupKeydown()" [title]="title | async" [ariaLabel]="title | async" > @@ -17,12 +19,20 @@ *ngFor="let color of colorGroup; trackBy: trackByIndex" [ngStyle]="{ backgroundColor: color, color: getContrastYIQ(color) }" [title]="color" - (click)="onColorSelect($event, color)" + (mousedown)="onColorSelectMouseClick($event, color)" + (keydown.enter)="onColorSelectKeydown(color)" + (keydown.space)="onColorSelectKeydown(color)" [ngClass]="{ 'NgxEditor__Color--Active': activeColors.includes(color) }" > - diff --git a/projects/ngx-editor/src/lib/modules/menu/color-picker/color-picker.component.ts b/projects/ngx-editor/src/lib/modules/menu/color-picker/color-picker.component.ts index 99e67b5b..9b03a8af 100644 --- a/projects/ngx-editor/src/lib/modules/menu/color-picker/color-picker.component.ts +++ b/projects/ngx-editor/src/lib/modules/menu/color-picker/color-picker.component.ts @@ -65,35 +65,50 @@ export class ColorPickerComponent implements OnInit, OnDestroy { this.showPopup = false; } - togglePopup(e: MouseEvent): void { + togglePopup(): void { + this.showPopup = !this.showPopup; + } + + onTogglePopupMouseClick(e: MouseEvent): void { e.preventDefault(); if (e.button !== 0) { return; } - this.showPopup = !this.showPopup; + this.togglePopup(); } - remove(e: MouseEvent): void { - e.preventDefault(); + onTogglePopupKeydown(): void { + this.togglePopup(); + } + + remove(): void { const { state, dispatch } = this.editorView; this.command.remove()(state, dispatch); this.hidePopup(); } - trackByIndex(index: number): number { - return index; - } - - onColorSelect(e: MouseEvent, color: string): void { + onRemoveMouseClick(e: MouseEvent): void { e.preventDefault(); if (e.button !== 0) { return; } + e.preventDefault(); + } + + onRemoveKeydown(): void { + this.remove(); + } + + trackByIndex(index: number): number { + return index; + } + + selectColor(color:string):void { const { state, dispatch } = this.editorView; if (this.type === 'text_color') { @@ -111,6 +126,20 @@ export class ColorPickerComponent implements OnInit, OnDestroy { this.hidePopup(); } + onColorSelectMouseClick(e: MouseEvent, color: string): void { + e.preventDefault(); + + if (e.button !== 0) { + return; + } + + this.selectColor(color); + } + + onColorSelectKeydown(color: string): void { + this.selectColor(color); + } + private update = (view: EditorView) => { const { state } = view; this.canExecute = this.command.canExecute(state); diff --git a/projects/ngx-editor/src/lib/modules/menu/dropdown/dropdown.component.html b/projects/ngx-editor/src/lib/modules/menu/dropdown/dropdown.component.html index ea2f73eb..1c7590b6 100644 --- a/projects/ngx-editor/src/lib/modules/menu/dropdown/dropdown.component.html +++ b/projects/ngx-editor/src/lib/modules/menu/dropdown/dropdown.component.html @@ -4,7 +4,9 @@ [class.NgxEditor__Dropdown--Selected]="isSelected" [disabled]="isDropdownDisabled" [class.NgxEditor--Disabled]="isDropdownDisabled" - (click)="toggleDropdown($event)" + (mousedown)="onToggleDropdownMouseClick($event)" + (keydown.enter)="onToggleDropdownKeydown()" + (keydown.space)="onToggleDropdownKeydown()" [ariaLabel]="getName(activeItem || group) | async" aria-haspopup="listbox" [ariaExpanded]="isDropdownOpen" @@ -17,7 +19,9 @@ type="button" class="NgxEditor__Dropdown--Item" *ngFor="let item of items; trackBy: trackByIndex" - (click)="onClick($event, item)" + (mousedown)="onDropdownItemMouseClick($event, item)" + (keydown.enter)="onDropdownItemKeydown($event, item)" + (keydown.space)="onDropdownItemKeydown($event, item)" [ngClass]="{ 'NgxEditor__Dropdown--Active': item === activeItem, 'NgxEditor--Disabled': disabledItems.includes(item) diff --git a/projects/ngx-editor/src/lib/modules/menu/dropdown/dropdown.component.ts b/projects/ngx-editor/src/lib/modules/menu/dropdown/dropdown.component.ts index acd7eac0..31a6d2d1 100644 --- a/projects/ngx-editor/src/lib/modules/menu/dropdown/dropdown.component.ts +++ b/projects/ngx-editor/src/lib/modules/menu/dropdown/dropdown.component.ts @@ -51,20 +51,40 @@ export class DropdownComponent implements OnInit, OnDestroy { return this.ngxeService.locals.get(key); } - getIsDropdownActive(item:string): boolean { + getIsDropdownActive(item: string): boolean { return this.activeItem === item; } - toggleDropdown(e: MouseEvent): void { - e.preventDefault(); + toggleDropdown(): void { this.isDropdownOpen = !this.isDropdownOpen; } + onToggleDropdownMouseClick(e: MouseEvent): void { + e.preventDefault(); + + if (e.button !== 0) { + return; + } + + this.toggleDropdown(); + } + + onToggleDropdownKeydown(): void { + this.toggleDropdown(); + } + trackByIndex(index: number): number { return index; } - onClick(e: MouseEvent, item: TBHeadingItems): void { + selectItem(item: TBHeadingItems): void { + const command = ToggleCommands[item]; + const { state, dispatch } = this.editorView; + command.toggle()(state, dispatch); + this.isDropdownOpen = false; + } + + onDropdownItemMouseClick(e: MouseEvent, item: TBHeadingItems): void { e.preventDefault(); // consider only left click @@ -72,10 +92,13 @@ export class DropdownComponent implements OnInit, OnDestroy { return; } - const command = ToggleCommands[item]; - const { state, dispatch } = this.editorView; - command.toggle()(state, dispatch); - this.isDropdownOpen = false; + this.selectItem(item); + } + + onDropdownItemKeydown(event: Event, item: TBHeadingItems): void { + const e = event as KeyboardEvent; + e.preventDefault(); + this.selectItem(item); } private update = (view: EditorView) => { diff --git a/projects/ngx-editor/src/lib/modules/menu/image/image.component.html b/projects/ngx-editor/src/lib/modules/menu/image/image.component.html index c16f6d75..7ba70892 100644 --- a/projects/ngx-editor/src/lib/modules/menu/image/image.component.html +++ b/projects/ngx-editor/src/lib/modules/menu/image/image.component.html @@ -3,7 +3,9 @@ class="NgxEditor__MenuItem--Icon" [class.NgxEditor__MenuItem--Active]="isActive || showPopup" [innerHTML]="icon | sanitizeHtml" - (click)="onMouseDown($event)" + (mousedown)="onTogglePopupMouseClick($event)" + (keydown.enter)="onTogglePopupKeydown()" + (keydown.space)="onTogglePopupKeydown()" [title]="getLabel('insertImage') | async" [ariaLabel]="getLabel('insertImage') | async" aria-haspopup="dialog" diff --git a/projects/ngx-editor/src/lib/modules/menu/image/image.component.ts b/projects/ngx-editor/src/lib/modules/menu/image/image.component.ts index 64c3366a..0bf8d67a 100644 --- a/projects/ngx-editor/src/lib/modules/menu/image/image.component.ts +++ b/projects/ngx-editor/src/lib/modules/menu/image/image.component.ts @@ -72,11 +72,7 @@ export class ImageComponent implements OnInit, OnDestroy { }); } - onMouseDown(e: MouseEvent): void { - if (e.button !== 0) { - return; - } - + togglePopup(): void { this.showPopup = !this.showPopup; if (this.showPopup) { @@ -84,6 +80,18 @@ export class ImageComponent implements OnInit, OnDestroy { } } + onTogglePopupMouseClick(e:MouseEvent): void { + if (e.button !== 0) { + return; + } + + this.togglePopup(); + } + + onTogglePopupKeydown(): void { + this.togglePopup(); + } + private fillForm(): void { const { state } = this.editorView; const { selection } = state; diff --git a/projects/ngx-editor/src/lib/modules/menu/insert-command/insert-command.component.html b/projects/ngx-editor/src/lib/modules/menu/insert-command/insert-command.component.html index 50c771c1..33644dd2 100644 --- a/projects/ngx-editor/src/lib/modules/menu/insert-command/insert-command.component.html +++ b/projects/ngx-editor/src/lib/modules/menu/insert-command/insert-command.component.html @@ -3,7 +3,9 @@ [disabled]="disabled" [class.NgxEditor--Disabled]="disabled" [innerHTML]="html | sanitizeHtml" - (click)="insert($event)" + (mousedown)="onMouseClick($event)" + (keydown.enter)="onKeydown()" + (keydown.space)="onKeydown()" [title]="getTitle(name) | async" [ariaLabel]="getTitle(name) | async" > diff --git a/projects/ngx-editor/src/lib/modules/menu/insert-command/insert-command.component.ts b/projects/ngx-editor/src/lib/modules/menu/insert-command/insert-command.component.ts index 697c6e16..bd1b776e 100644 --- a/projects/ngx-editor/src/lib/modules/menu/insert-command/insert-command.component.ts +++ b/projects/ngx-editor/src/lib/modules/menu/insert-command/insert-command.component.ts @@ -31,13 +31,21 @@ export class InsertCommandComponent implements OnInit, OnDestroy { private menuService: MenuService, ) { } - insert(e: MouseEvent): void { + onMouseClick(e: MouseEvent): void { e.preventDefault(); if (e.button !== 0) { return; } + this.insert(); + } + + onKeydown(): void { + this.insert(); + } + + insert(): void { const { state, dispatch } = this.editorView; const command = InsertCommands[this.name]; command.insert()(state, dispatch); diff --git a/projects/ngx-editor/src/lib/modules/menu/link/link.component.html b/projects/ngx-editor/src/lib/modules/menu/link/link.component.html index d7bee8eb..02c9b47e 100644 --- a/projects/ngx-editor/src/lib/modules/menu/link/link.component.html +++ b/projects/ngx-editor/src/lib/modules/menu/link/link.component.html @@ -5,7 +5,9 @@ [class.NgxEditor--Disabled]="!canExecute" [disabled]="!canExecute" [innerHTML]="icon | sanitizeHtml" - (click)="onMouseDown($event)" + (mousedown)="onTogglePopupMouseClick($event)" + (keydown.enter)="onTogglePopupKeydown()" + (keydown.space)="onTogglePopupKeydown()" [title]="title | async" [ariaLabel]="title | async" aria-haspopup="dialog" diff --git a/projects/ngx-editor/src/lib/modules/menu/link/link.component.ts b/projects/ngx-editor/src/lib/modules/menu/link/link.component.ts index adeeece6..cc87cfb1 100644 --- a/projects/ngx-editor/src/lib/modules/menu/link/link.component.ts +++ b/projects/ngx-editor/src/lib/modules/menu/link/link.component.ts @@ -64,7 +64,7 @@ export class LinkComponent implements OnInit, OnDestroy { @HostListener('document:mousedown', ['$event']) onDocumentClick(e: MouseEvent): void { if (!this.el.nativeElement.contains(e.target) && this.showPopup) { - this.hideForm(); + this.hidePopup(); } } @@ -76,7 +76,7 @@ export class LinkComponent implements OnInit, OnDestroy { return this.ngxeService.locals.get(key); } - private hideForm(): void { + private hidePopup(): void { this.showPopup = false; this.form.reset({ href: '', @@ -86,11 +86,7 @@ export class LinkComponent implements OnInit, OnDestroy { this.text.enable(); } - onMouseDown(e: MouseEvent): void { - if (e.button !== 0) { - return; - } - + togglePopup(): void { const { state, dispatch } = this.editorView; if (this.isActive) { @@ -104,6 +100,18 @@ export class LinkComponent implements OnInit, OnDestroy { } } + onTogglePopupMouseClick(e:MouseEvent): void { + if (e.button !== 0) { + return; + } + + this.togglePopup(); + } + + onTogglePopupKeydown(): void { + this.togglePopup(); + } + private setText = () => { const { state: { selection, doc } } = this.editorView; const { empty, from, to } = selection; @@ -145,7 +153,7 @@ export class LinkComponent implements OnInit, OnDestroy { } else { LinkCommand.update(attrs)(state, dispatch); } - this.hideForm(); + this.hidePopup(); } ngOnInit(): void { diff --git a/projects/ngx-editor/src/lib/modules/menu/toggle-command/toggle-command.component.html b/projects/ngx-editor/src/lib/modules/menu/toggle-command/toggle-command.component.html index c6077b24..be05bbed 100644 --- a/projects/ngx-editor/src/lib/modules/menu/toggle-command/toggle-command.component.html +++ b/projects/ngx-editor/src/lib/modules/menu/toggle-command/toggle-command.component.html @@ -5,7 +5,9 @@ [class.NgxEditor--Disabled]="disabled" [disabled]="disabled" [innerHTML]="html | sanitizeHtml" - (click)="toggle($event)" + (mousedown)="onMouseClick($event)" + (keydown.enter)="onKeydown()" + (keydown.space)="onKeydown()" [title]="getTitle(name) | async" [ariaLabel]="getTitle(name) | async" > diff --git a/projects/ngx-editor/src/lib/modules/menu/toggle-command/toggle-command.component.ts b/projects/ngx-editor/src/lib/modules/menu/toggle-command/toggle-command.component.ts index 53a81c4a..3c9c761d 100644 --- a/projects/ngx-editor/src/lib/modules/menu/toggle-command/toggle-command.component.ts +++ b/projects/ngx-editor/src/lib/modules/menu/toggle-command/toggle-command.component.ts @@ -32,16 +32,24 @@ export class ToggleCommandComponent implements OnInit, OnDestroy { private menuService: MenuService, ) { } - toggle(e: MouseEvent): void { + toggle(): void { + const { state, dispatch } = this.editorView; + const command = ToggleCommands[this.name]; + command.toggle()(state, dispatch); + } + + onMouseClick(e: MouseEvent): void { e.preventDefault(); if (e.button !== 0) { return; } - const { state, dispatch } = this.editorView; - const command = ToggleCommands[this.name]; - command.toggle()(state, dispatch); + this.toggle(); + } + + onKeydown(): void { + this.toggle(); } update = (view: EditorView): void => {