diff --git a/src/app/components/context-menu/context-menu.component.html b/src/app/components/context-menu/context-menu.component.html index 7283cb02c..d9ee883bf 100644 --- a/src/app/components/context-menu/context-menu.component.html +++ b/src/app/components/context-menu/context-menu.component.html @@ -1,50 +1,54 @@ - +
+ - - - - - + + + + - - - + + + - - + + - - - - - - + + + + + + - - + + + - - + +
diff --git a/src/app/components/context-menu/context-menu.component.ts b/src/app/components/context-menu/context-menu.component.ts index 4fae5f538..f72046871 100644 --- a/src/app/components/context-menu/context-menu.component.ts +++ b/src/app/components/context-menu/context-menu.component.ts @@ -30,10 +30,10 @@ import { OnDestroy, HostListener, ViewChild, - AfterViewInit + AfterViewInit, + Inject } from '@angular/core'; import { MatMenuTrigger } from '@angular/material/menu'; - import { AppExtensionService } from '../../extensions/extension.service'; import { AppStore, getAppSelection } from '@alfresco/aca-shared/store'; import { Store } from '@ngrx/store'; @@ -41,6 +41,8 @@ import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { ContentActionRef } from '@alfresco/adf-extensions'; import { ContextMenuOverlayRef } from './context-menu-overlay'; +import { CONTEXT_MENU_DIRECTION } from './direction.token'; +import { Directionality } from '@angular/cdk/bidi'; @Component({ selector: 'aca-context-menu', @@ -70,7 +72,8 @@ export class ContextMenuComponent implements OnInit, OnDestroy, AfterViewInit { constructor( private contextMenuOverlayRef: ContextMenuOverlayRef, private extensions: AppExtensionService, - private store: Store + private store: Store, + @Inject(CONTEXT_MENU_DIRECTION) public direction: Directionality ) {} onClickOutsideEvent() { diff --git a/src/app/components/context-menu/context-menu.service.spec.ts b/src/app/components/context-menu/context-menu.service.spec.ts index 5ac488db6..a21d19e39 100644 --- a/src/app/components/context-menu/context-menu.service.spec.ts +++ b/src/app/components/context-menu/context-menu.service.spec.ts @@ -31,36 +31,85 @@ import { of } from 'rxjs'; import { CoreModule } from '@alfresco/adf-core'; import { ContextMenuService } from './context-menu.service'; import { ContextMenuModule } from './context-menu.module'; +import { UserPreferencesService } from '@alfresco/adf-core'; describe('ContextMenuService', () => { let contextMenuService; + let overlay; + let injector; + let userPreferencesService; const overlayConfig = { hasBackdrop: false, backdropClass: '', - panelClass: 'test-panel' + panelClass: 'test-panel', + source: { + x: 1, + y: 1 + } }; beforeEach(() => { TestBed.configureTestingModule({ imports: [CoreModule.forRoot(), ContextMenuModule], - providers: [Overlay, { provide: Store, useValue: { select: () => of() } }] + providers: [ + Overlay, + { provide: Store, useValue: { select: () => of() } }, + UserPreferencesService + ] }); - const injector = TestBed.get(Injector); - const overlay = TestBed.get(Overlay); - - contextMenuService = new ContextMenuService(injector, overlay); + injector = TestBed.get(Injector); + overlay = TestBed.get(Overlay); + userPreferencesService = TestBed.get(UserPreferencesService); }); it('should create a custom overlay', () => { + contextMenuService = new ContextMenuService( + injector, + overlay, + userPreferencesService + ); + contextMenuService.open(overlayConfig); expect(document.querySelector('.test-panel')).not.toBe(null); }); it('should render component', () => { + contextMenuService = new ContextMenuService( + injector, + overlay, + userPreferencesService + ); + contextMenuService.open(overlayConfig); expect(document.querySelector('aca-context-menu')).not.toBe(null); }); + + it('should have default LTR direction value', () => { + contextMenuService = new ContextMenuService( + injector, + overlay, + userPreferencesService + ); + + contextMenuService.open(overlayConfig); + + expect(document.body.querySelector('div[dir="ltr"]')).not.toBe(null); + }); + + it('should change direction on textOrientation event', () => { + spyOn(userPreferencesService, 'select').and.returnValue(of('rtl')); + + contextMenuService = new ContextMenuService( + injector, + overlay, + userPreferencesService + ); + + contextMenuService.open(overlayConfig); + + expect(document.body.querySelector('div[dir="rtl"]')).not.toBe(null); + }); }); diff --git a/src/app/components/context-menu/context-menu.service.ts b/src/app/components/context-menu/context-menu.service.ts index bd9a71fea..4787e43e9 100644 --- a/src/app/components/context-menu/context-menu.service.ts +++ b/src/app/components/context-menu/context-menu.service.ts @@ -1,15 +1,55 @@ -import { Injectable, Injector, ComponentRef, ElementRef } from '@angular/core'; +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2019 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { Injectable, Injector, ComponentRef } from '@angular/core'; import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; import { ComponentPortal, PortalInjector } from '@angular/cdk/portal'; import { ContextMenuOverlayRef } from './context-menu-overlay'; import { ContextMenuComponent } from './context-menu.component'; import { ContextmenuOverlayConfig } from './interfaces'; +import { UserPreferencesService } from '@alfresco/adf-core'; +import { Directionality } from '@angular/cdk/bidi'; +import { CONTEXT_MENU_DIRECTION } from './direction.token'; @Injectable({ providedIn: 'root' }) export class ContextMenuService { - constructor(private injector: Injector, private overlay: Overlay) {} + private direction: Directionality; + + constructor( + private injector: Injector, + private overlay: Overlay, + private userPreferenceService: UserPreferencesService + ) { + this.userPreferenceService + .select('textOrientation') + .subscribe(textOrientation => { + this.direction = textOrientation; + }); + } open(config: ContextmenuOverlayConfig) { const overlay = this.createOverlay(config); @@ -49,56 +89,33 @@ export class ContextMenuService { const injectionTokens = new WeakMap(); injectionTokens.set(ContextMenuOverlayRef, contextmenuOverlayRef); + injectionTokens.set(CONTEXT_MENU_DIRECTION, this.direction); return new PortalInjector(this.injector, injectionTokens); } private getOverlayConfig(config: ContextmenuOverlayConfig): OverlayConfig { - const fakeElement: any = { - getBoundingClientRect: (): ClientRect => ({ - bottom: config.source.clientY, - height: 0, - left: config.source.clientX, - right: config.source.clientX, - top: config.source.clientY, - width: 0 - }) - }; + const { x, y } = config.source; const positionStrategy = this.overlay .position() - .connectedTo( - new ElementRef(fakeElement), - { originX: 'start', originY: 'bottom' }, - { overlayX: 'start', overlayY: 'top' } - ) - .withFallbackPosition( - { originX: 'start', originY: 'top' }, - { overlayX: 'start', overlayY: 'bottom' } - ) - .withFallbackPosition( - { originX: 'end', originY: 'top' }, - { overlayX: 'start', overlayY: 'top' } - ) - .withFallbackPosition( - { originX: 'start', originY: 'top' }, - { overlayX: 'end', overlayY: 'top' } - ) - .withFallbackPosition( - { originX: 'end', originY: 'center' }, - { overlayX: 'start', overlayY: 'center' } - ) - .withFallbackPosition( - { originX: 'start', originY: 'center' }, - { overlayX: 'end', overlayY: 'center' } - ); + .flexibleConnectedTo({ x, y }) + .withPositions([ + { + originX: 'end', + originY: 'bottom', + overlayX: 'end', + overlayY: 'top' + } + ]); const overlayConfig = new OverlayConfig({ hasBackdrop: config.hasBackdrop, backdropClass: config.backdropClass, panelClass: config.panelClass, scrollStrategy: this.overlay.scrollStrategies.close(), - positionStrategy + positionStrategy, + direction: this.direction }); return overlayConfig; diff --git a/src/app/components/context-menu/direction.token.ts b/src/app/components/context-menu/direction.token.ts new file mode 100644 index 000000000..07eca2e5c --- /dev/null +++ b/src/app/components/context-menu/direction.token.ts @@ -0,0 +1,34 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2019 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { InjectionToken } from '@angular/core'; + +export const CONTEXT_MENU_DIRECTION = new InjectionToken( + 'CONTEXT_MENU_DIRECTION', + { + providedIn: 'root', + factory: () => 'ltr' + } +);