Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ACA] Context menu - librarie files fix #815

Merged
merged 1 commit into from
Nov 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
OnDestroy
} from '@angular/core';
import { fromEvent, Subscription } from 'rxjs';
import { delay } from 'rxjs/operators';
import { filter } from 'rxjs/operators';

@Directive({
selector: '[acaContextMenuOutsideEvent]'
Expand All @@ -22,7 +22,7 @@ export class OutsideEventDirective implements OnInit, OnDestroy {
ngOnInit() {
this.subscriptions = this.subscriptions.concat([
fromEvent(document.body, 'click')
.pipe(delay(1))
.pipe(filter(event => !this.findAncestor(event.target as Element)))
.subscribe(() => this.clickOutside.next())
]);
}
Expand All @@ -31,4 +31,15 @@ export class OutsideEventDirective implements OnInit, OnDestroy {
this.subscriptions.forEach(subscription => subscription.unsubscribe());
this.subscriptions = [];
}

private findAncestor(el: Element): boolean {
const className = 'aca-context-menu';

if (el.classList.contains(className)) {
return true;
}
// tslint:disable-next-line:curly
while ((el = el.parentElement) && !el.classList.contains(className));
return !!el;
}
}
112 changes: 49 additions & 63 deletions src/app/components/context-menu/context-menu.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,24 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/

import { Directive, HostListener, Input } from '@angular/core';
import {
Directive,
HostListener,
Input,
OnInit,
OnDestroy
} from '@angular/core';
import { ContextMenuOverlayRef } from './context-menu-overlay';
import { ContextMenuService } from './context-menu.service';
import { DocumentListComponent } from '@alfresco/adf-content-services';
import { SetSelectedNodesAction } from '../../store/actions';
import { Store } from '@ngrx/store';
import { AppStore } from '../../store/states/app.state';
import { DataRow } from '@alfresco/adf-core';
import { MinimalNodeEntity } from 'alfresco-js-api';
import { debounceTime } from 'rxjs/operators';
import { Subject, fromEvent, Subscription } from 'rxjs';

@Directive({
selector: '[acaContextActions]'
})
export class ContextActionsDirective {
export class ContextActionsDirective implements OnInit, OnDestroy {
private execute$: Subject<any> = new Subject();
private subscriptions: Subscription[] = [];
private overlayRef: ContextMenuOverlayRef = null;

// tslint:disable-next-line:no-input-rename
Expand All @@ -49,40 +53,44 @@ export class ContextActionsDirective {
event.preventDefault();

if (this.enabled) {
this.execute(event);
const target = this.getTarget(event);
if (target) {
this.execute(event, target);
}
}
}
}

constructor(
private documentList: DocumentListComponent,
private store: Store<AppStore>,
private contextMenuService: ContextMenuService
) {}
constructor(private contextMenuService: ContextMenuService) {}

private execute(event: MouseEvent) {
// todo: review this in ADF
const selected = this.getSelectedRow(event);
ngOnInit() {
this.subscriptions.push(
fromEvent(document.body, 'contextmenu').subscribe(() => {
if (this.overlayRef) {
this.overlayRef.close();
}
}),

if (selected) {
if (!this.isInSelection(selected)) {
this.clearSelection();

this.documentList.dataTable.selectRow(selected, true);
this.documentList.selection.push((<any>selected).node);
this.execute$.pipe(debounceTime(300)).subscribe((event: MouseEvent) => {
this.render(event);
})
);
}

this.updateSelection();
}
ngOnDestroy() {
this.subscriptions.forEach(subscription => subscription.unsubscribe());
this.subscriptions = [];
this.execute$ = null;
}

this.render(event);
execute(event: MouseEvent, target: Element) {
if (!this.isSelected(target)) {
target.dispatchEvent(new MouseEvent('click'));
}
this.execute$.next(event);
}

private render(event: MouseEvent) {
if (this.overlayRef) {
this.overlayRef.close();
}

this.overlayRef = this.contextMenuService.open({
source: event,
hasBackdrop: false,
Expand All @@ -91,46 +99,24 @@ export class ContextActionsDirective {
});
}

private updateSelection() {
this.store.dispatch(
new SetSelectedNodesAction(this.documentList.selection)
);
}

private isInSelection(row: DataRow): MinimalNodeEntity {
return this.documentList.selection.find(
selected => row.getValue('name') === selected.entry.name
);
private getTarget(event: MouseEvent): Element {
return this.findAncestor(<Element>event.target, 'adf-datatable-table-cell');
}

private getSelectedRow(event): DataRow {
const rowElement = this.findAncestor(
<HTMLElement>event.target,
'adf-datatable-row'
);

if (!rowElement) {
return null;
private isSelected(target): boolean {
if (!target) {
return false;
}

const rowName = rowElement
.querySelector('.adf-data-table-cell--text .adf-datatable-cell')
.textContent.trim();

return this.documentList.data
.getRows()
.find((row: DataRow) => row.getValue('name') === rowName);
}

private clearSelection() {
this.documentList.data.getRows().map((row: DataRow) => {
return this.documentList.dataTable.selectRow(row, false);
});

this.documentList.selection = [];
return this.findAncestor(target, 'adf-datatable-row').classList.contains(
'is-selected'
);
}

private findAncestor(el: Element, className: string): Element {
if (el.classList.contains(className)) {
return el;
}
// tslint:disable-next-line:curly
while ((el = el.parentElement) && !el.classList.contains(className));
return el;
Expand Down
33 changes: 17 additions & 16 deletions src/app/components/context-menu/context-menu.directives.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,39 +24,40 @@
*/

import { ContextActionsDirective } from './context-menu.directive';
import { fakeAsync, tick } from '@angular/core/testing';

describe('ContextActionsDirective', () => {
let directive;
const contextMenuServiceMock = <any>{
open: jasmine.createSpy('open')
};
const storeMock = <any>{};
const documentListMock = <any>{};

beforeEach(() => {
directive = new ContextActionsDirective(
documentListMock,
storeMock,
contextMenuServiceMock
);
directive = new ContextActionsDirective(contextMenuServiceMock);
});

it('should not render context menu when disable property is false', () => {
it('should not render context menu when `enabled` property is false', () => {
directive.enabled = false;
spyOn(directive, 'getSelectedRow').and.returnValue({});

directive.onContextMenuEvent(new MouseEvent('contextmenu'));

expect(contextMenuServiceMock.open).not.toHaveBeenCalled();
});

it('should render context menu when disable property is true', () => {
directive.enabled = true;
spyOn(directive, 'getSelectedRow').and.returnValue({});
spyOn(directive, 'isInSelection').and.returnValue(true);
it('should call service to render context menu', fakeAsync(() => {
const el = document.createElement('div');
el.className =
'adf-data-table-cell adf-datatable-table-cell adf-datatable-row';

directive.onContextMenuEvent(new MouseEvent('contextmenu'));
const fragment = document.createDocumentFragment();
fragment.appendChild(el);
const target = fragment.querySelector('div');

directive.ngOnInit();

directive.onContextMenuEvent(<any>{ preventDefault: () => {}, target });

tick(500);

expect(contextMenuServiceMock.open).toHaveBeenCalled();
});
}));
});