mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-09 18:03:21 +00:00
ACS-11160: improve context menu overlay service implementation (#11661)
* refactor: improve context menu overlay service implementation - Introduced a new interface for overlay references with backdrop handling. - Updated event listener for backdrop clicks to use the new interface, enhancing type safety. - Refactored the injector creation method to utilize the Injector.create method for better clarity and maintainability. - Improved the fake element's type definition in the getOverlayConfig method for better type inference. These changes enhance the overall robustness and readability of the context menu overlay service. * refactor: enhance type safety in context menu components - Introduced the ContextMenuItem interface to improve type definitions for context menu items. - Updated the ContextMenuListComponent to use ContextMenuItem for links and menu item parameters, enhancing clarity and maintainability. - Modified CONTEXT_MENU_DATA injection token to specify ContextMenuItem[] for better type safety. - Adjusted the ContextMenuOverlayConfig interface to accept an array of ContextMenuItem, ensuring consistent data handling. These changes improve the overall robustness and readability of the context menu implementation. [ci:force] * refactor: remove unnecessary eslint-disable comments in category management and search components - Eliminated eslint-disable comments related to no-underscore-dangle in the CategoriesManagementComponent and SearchControlComponent tests, improving code clarity and adherence to linting rules. - This change enhances the overall readability of the test files by ensuring consistent coding standards. * refactor: simplify backdrop click handling in context menu overlay service - Removed the unnecessary backdrop click handling logic, replacing it with a direct call to close the overlay reference. This change enhances code clarity and reduces complexity in the event listener implementation. * refactor: streamline backdrop handling in context menu overlay service - Removed the custom interface for overlay references with backdrop handling, simplifying the code. - Updated the event listener for backdrop clicks to directly use the overlay's backdropElement, enhancing clarity and maintainability. - This change improves the overall robustness of the context menu overlay service.
This commit is contained in:
-2
@@ -328,7 +328,6 @@ describe('CategoriesManagementComponent', () => {
|
|||||||
const categoriesChangeSpy = spyOn(component.categoriesChange, 'emit').and.callThrough();
|
const categoriesChangeSpy = spyOn(component.categoriesChange, 'emit').and.callThrough();
|
||||||
typeCategory('test');
|
typeCategory('test');
|
||||||
const options = getExistingCategoriesList();
|
const options = getExistingCategoriesList();
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
|
||||||
options[0].click();
|
options[0].click();
|
||||||
|
|
||||||
expect(component.categories.length).toBe(3);
|
expect(component.categories.length).toBe(3);
|
||||||
@@ -342,7 +341,6 @@ describe('CategoriesManagementComponent', () => {
|
|||||||
it('should remove selected category from categories list and add it back to existing categories', fakeAsync(() => {
|
it('should remove selected category from categories list and add it back to existing categories', fakeAsync(() => {
|
||||||
typeCategory('test');
|
typeCategory('test');
|
||||||
const options = getExistingCategoriesList();
|
const options = getExistingCategoriesList();
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
|
||||||
options[0].click();
|
options[0].click();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { NgForOf, NgIf } from '@angular/common';
|
|||||||
import { TranslatePipe } from '@ngx-translate/core';
|
import { TranslatePipe } from '@ngx-translate/core';
|
||||||
import { DOWN_ARROW, UP_ARROW } from '@angular/cdk/keycodes';
|
import { DOWN_ARROW, UP_ARROW } from '@angular/cdk/keycodes';
|
||||||
import { IconModule } from '../icon/icon.module';
|
import { IconModule } from '../icon/icon.module';
|
||||||
|
import { ContextMenuItem } from './interfaces';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-context-menu',
|
selector: 'adf-context-menu',
|
||||||
@@ -42,10 +43,10 @@ import { IconModule } from '../icon/icon.module';
|
|||||||
export class ContextMenuListComponent implements AfterViewInit {
|
export class ContextMenuListComponent implements AfterViewInit {
|
||||||
private keyManager: FocusKeyManager<MatMenuItem>;
|
private keyManager: FocusKeyManager<MatMenuItem>;
|
||||||
@ViewChildren(MatMenuItem) items: QueryList<MatMenuItem>;
|
@ViewChildren(MatMenuItem) items: QueryList<MatMenuItem>;
|
||||||
links: any[];
|
links: ContextMenuItem[];
|
||||||
|
|
||||||
@HostListener('document:keydown.Escape', ['$event'])
|
@HostListener('document:keydown.Escape', ['$event'])
|
||||||
handleKeydownEscape(event: KeyboardEvent) {
|
handleKeydownEscape(event: Event) {
|
||||||
if (event) {
|
if (event) {
|
||||||
this.contextMenuOverlayRef.close();
|
this.contextMenuOverlayRef.close();
|
||||||
}
|
}
|
||||||
@@ -63,12 +64,12 @@ export class ContextMenuListComponent implements AfterViewInit {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(ContextMenuOverlayRef) private contextMenuOverlayRef: ContextMenuOverlayRef,
|
@Inject(ContextMenuOverlayRef) private contextMenuOverlayRef: ContextMenuOverlayRef,
|
||||||
@Optional() @Inject(CONTEXT_MENU_DATA) private data: any
|
@Optional() @Inject(CONTEXT_MENU_DATA) private data: ContextMenuItem[]
|
||||||
) {
|
) {
|
||||||
this.links = this.data;
|
this.links = this.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMenuItemClick(event: Event, menuItem: any) {
|
onMenuItemClick(event: Event, menuItem: ContextMenuItem) {
|
||||||
if (menuItem?.model?.disabled) {
|
if (menuItem?.model?.disabled) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
import { Injectable, Injector, ElementRef, ComponentRef } from '@angular/core';
|
import { Injectable, Injector, ElementRef, ComponentRef } from '@angular/core';
|
||||||
import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
|
import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
|
||||||
import { PortalInjector, ComponentPortal } from '@angular/cdk/portal';
|
import { ComponentPortal } from '@angular/cdk/portal';
|
||||||
import { ContextMenuOverlayRef } from './context-menu-overlay';
|
import { ContextMenuOverlayRef } from './context-menu-overlay';
|
||||||
import { ContextMenuOverlayConfig } from './interfaces';
|
import { ContextMenuOverlayConfig } from './interfaces';
|
||||||
import { CONTEXT_MENU_DATA } from './context-menu.tokens';
|
import { CONTEXT_MENU_DATA } from './context-menu.tokens';
|
||||||
@@ -33,7 +33,10 @@ const DEFAULT_CONFIG: ContextMenuOverlayConfig = {
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class ContextMenuOverlayService {
|
export class ContextMenuOverlayService {
|
||||||
constructor(private injector: Injector, private overlay: Overlay) {}
|
constructor(
|
||||||
|
private injector: Injector,
|
||||||
|
private overlay: Overlay
|
||||||
|
) {}
|
||||||
|
|
||||||
open(config: ContextMenuOverlayConfig): ContextMenuOverlayRef {
|
open(config: ContextMenuOverlayConfig): ContextMenuOverlayRef {
|
||||||
const overlayConfig = { ...DEFAULT_CONFIG, ...config };
|
const overlayConfig = { ...DEFAULT_CONFIG, ...config };
|
||||||
@@ -47,14 +50,12 @@ export class ContextMenuOverlayService {
|
|||||||
overlay.backdropClick().subscribe(() => overlayRef.close());
|
overlay.backdropClick().subscribe(() => overlayRef.close());
|
||||||
|
|
||||||
// prevent native contextmenu on overlay element if config.hasBackdrop is true
|
// prevent native contextmenu on overlay element if config.hasBackdrop is true
|
||||||
if (overlayConfig.hasBackdrop) {
|
if (overlayConfig.hasBackdrop && overlay.backdropElement) {
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
overlay.backdropElement.addEventListener(
|
||||||
(overlay as any).backdropElement.addEventListener(
|
|
||||||
'contextmenu',
|
'contextmenu',
|
||||||
(event) => {
|
(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
overlayRef.close();
|
||||||
(overlay as any)._backdropClick.next(null);
|
|
||||||
},
|
},
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
@@ -77,28 +78,32 @@ export class ContextMenuOverlayService {
|
|||||||
return containerRef.instance;
|
return containerRef.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createInjector(config: ContextMenuOverlayConfig, contextMenuOverlayRef: ContextMenuOverlayRef): PortalInjector {
|
private createInjector(config: ContextMenuOverlayConfig, contextMenuOverlayRef: ContextMenuOverlayRef): Injector {
|
||||||
const injectionTokens = new WeakMap();
|
return Injector.create({
|
||||||
|
parent: this.injector,
|
||||||
injectionTokens.set(ContextMenuOverlayRef, contextMenuOverlayRef);
|
providers: [
|
||||||
injectionTokens.set(CONTEXT_MENU_DATA, config.data);
|
{ provide: ContextMenuOverlayRef, useValue: contextMenuOverlayRef },
|
||||||
|
{ provide: CONTEXT_MENU_DATA, useValue: config.data }
|
||||||
return new PortalInjector(this.injector, injectionTokens);
|
]
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private getOverlayConfig(config: ContextMenuOverlayConfig): OverlayConfig {
|
private getOverlayConfig(config: ContextMenuOverlayConfig): OverlayConfig {
|
||||||
const { clientY, clientX } = config.source;
|
const { clientY, clientX } = config.source;
|
||||||
|
|
||||||
const fakeElement: any = {
|
const fakeElement: Pick<HTMLElement, 'getBoundingClientRect'> = {
|
||||||
getBoundingClientRect: (): ClientRect =>
|
getBoundingClientRect: (): DOMRect =>
|
||||||
({
|
({
|
||||||
bottom: clientY,
|
bottom: clientY,
|
||||||
height: 0,
|
height: 0,
|
||||||
left: clientX,
|
left: clientX,
|
||||||
right: clientX,
|
right: clientX,
|
||||||
top: clientY,
|
top: clientY,
|
||||||
width: 0
|
width: 0,
|
||||||
} as any)
|
x: clientX,
|
||||||
|
y: clientY,
|
||||||
|
toJSON: () => ({})
|
||||||
|
}) as DOMRect
|
||||||
};
|
};
|
||||||
|
|
||||||
const positionStrategy = this.overlay
|
const positionStrategy = this.overlay
|
||||||
|
|||||||
@@ -16,5 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { InjectionToken } from '@angular/core';
|
import { InjectionToken } from '@angular/core';
|
||||||
|
import { ContextMenuItem } from './interfaces';
|
||||||
|
|
||||||
export const CONTEXT_MENU_DATA = new InjectionToken<any>('CONTEXT_MENU_DATA');
|
export const CONTEXT_MENU_DATA = new InjectionToken<ContextMenuItem[]>('CONTEXT_MENU_DATA');
|
||||||
|
|||||||
@@ -20,5 +20,19 @@ export interface ContextMenuOverlayConfig {
|
|||||||
hasBackdrop?: boolean;
|
hasBackdrop?: boolean;
|
||||||
backdropClass?: string;
|
backdropClass?: string;
|
||||||
source?: MouseEvent;
|
source?: MouseEvent;
|
||||||
data?: any;
|
data?: ContextMenuItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContextMenuItem {
|
||||||
|
title?: string;
|
||||||
|
model?: {
|
||||||
|
title?: string;
|
||||||
|
visible?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
icon?: string;
|
||||||
|
tooltip?: string;
|
||||||
|
};
|
||||||
|
subject: {
|
||||||
|
next: (value: ContextMenuItem) => void;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user