mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-07-24 17:31:52 +00:00
[ACA-1614] DocumentList - context menu actions (#544)
* context menu * make same structure check * align naming * lazy loading support * update module import implementation * close context menu on Escape * focus and navigate context menu items * update with material cdk 'keycodes' name * changed module folder name
This commit is contained in:
@@ -39,7 +39,8 @@
|
||||
"unindent",
|
||||
"exif",
|
||||
"cardview",
|
||||
"webm"
|
||||
"webm",
|
||||
"keycodes"
|
||||
],
|
||||
"dictionaries": [
|
||||
"html",
|
||||
|
@@ -76,6 +76,7 @@ import { DirectivesModule } from './directives/directives.module';
|
||||
import { ToggleInfoDrawerComponent } from './components/toolbar/toggle-info-drawer/toggle-info-drawer.component';
|
||||
import { DocumentDisplayModeComponent } from './components/toolbar/document-display-mode/document-display-mode.component';
|
||||
import { ToggleFavoriteComponent } from './components/toolbar/toggle-favorite/toggle-favorite.component';
|
||||
import { ContextMenuModule } from './context-menu/context-menu.module';
|
||||
|
||||
export function setupExtensionServiceFactory(service: ExtensionService): Function {
|
||||
return () => service.load();
|
||||
@@ -98,6 +99,7 @@ export function setupExtensionServiceFactory(service: ExtensionService): Functio
|
||||
ExtensionsModule,
|
||||
|
||||
DirectivesModule,
|
||||
ContextMenuModule.forRoot(),
|
||||
AppInfoDrawerModule
|
||||
],
|
||||
declarations: [
|
||||
|
@@ -13,7 +13,10 @@
|
||||
</div>
|
||||
<div class="inner-layout__content">
|
||||
<div class="inner-layout__panel">
|
||||
<adf-document-list acaDocumentList #documentList
|
||||
<adf-document-list #documentList
|
||||
acaDocumentList
|
||||
acaContextActions
|
||||
[acaContextEnable]="selection.count"
|
||||
[display]="documentDisplayMode$ | async"
|
||||
currentFolderId="-favorites-"
|
||||
selectionMode="multiple"
|
||||
|
@@ -24,7 +24,10 @@
|
||||
[parentId]="node?.id"
|
||||
[disabled]="!canUpload">
|
||||
|
||||
<adf-document-list acaDocumentList #documentList
|
||||
<adf-document-list #documentList
|
||||
acaDocumentList
|
||||
acaContextActions
|
||||
[acaContextEnable]="selection.count"
|
||||
[display]="documentDisplayMode$ | async"
|
||||
[sorting]="[ 'modifiedAt', 'desc' ]"
|
||||
selectionMode="multiple"
|
||||
|
@@ -16,7 +16,10 @@
|
||||
|
||||
<div class="inner-layout__content">
|
||||
<div class="inner-layout__panel">
|
||||
<adf-document-list acaDocumentList #documentList
|
||||
<adf-document-list #documentList
|
||||
acaDocumentList
|
||||
acaContextActions
|
||||
[acaContextEnable]="selection.count"
|
||||
[display]="documentDisplayMode$ | async"
|
||||
currentFolderId="-mysites-"
|
||||
selectionMode="single"
|
||||
|
@@ -14,7 +14,10 @@
|
||||
|
||||
<div class="inner-layout__content">
|
||||
<div class="inner-layout__panel">
|
||||
<adf-document-list acaDocumentList #documentList
|
||||
<adf-document-list #documentList
|
||||
acaDocumentList
|
||||
acaContextActions
|
||||
[acaContextEnable]="selection.count"
|
||||
[display]="documentDisplayMode$ | async"
|
||||
currentFolderId="-recent-"
|
||||
selectionMode="multiple"
|
||||
|
@@ -14,7 +14,10 @@
|
||||
|
||||
<div class="inner-layout__content">
|
||||
<div class="inner-layout__panel">
|
||||
<adf-document-list acaDocumentList #documentList
|
||||
<adf-document-list #documentList
|
||||
acaDocumentList
|
||||
acaContextActions
|
||||
[acaContextEnable]="selection.count"
|
||||
[display]="documentDisplayMode$ | async"
|
||||
currentFolderId="-sharedlinks-"
|
||||
selectionMode="multiple"
|
||||
|
@@ -14,7 +14,10 @@
|
||||
|
||||
<div class="inner-layout__content">
|
||||
<div class="inner-layout__panel">
|
||||
<adf-document-list acaDocumentList #documentList
|
||||
<adf-document-list #documentList
|
||||
acaDocumentList
|
||||
acaContextActions
|
||||
[acaContextEnable]="selection.count"
|
||||
[display]="documentDisplayMode$ | async"
|
||||
currentFolderId="-trashcan-"
|
||||
selectionMode="multiple"
|
||||
|
52
src/app/context-menu/animations.ts
Normal file
52
src/app/context-menu/animations.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
/*!
|
||||
* @license
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* Copyright (C) 2005 - 2018 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
state,
|
||||
style,
|
||||
animate,
|
||||
transition,
|
||||
query,
|
||||
group,
|
||||
sequence
|
||||
} from '@angular/animations';
|
||||
|
||||
export const contextMenuAnimation = [
|
||||
state('void', style({
|
||||
opacity: 0,
|
||||
transform: 'scale(0.01, 0.01)'
|
||||
})),
|
||||
transition('void => *', sequence([
|
||||
query('.mat-menu-content', style({ opacity: 0 })),
|
||||
animate('100ms linear', style({ opacity: 1, transform: 'scale(1, 0.5)' })),
|
||||
group([
|
||||
query('.mat-menu-content', animate('400ms cubic-bezier(0.55, 0, 0.55, 0.2)',
|
||||
style({ opacity: 1 })
|
||||
)),
|
||||
animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({ transform: 'scale(1, 1)' })),
|
||||
])
|
||||
])),
|
||||
transition('* => void', animate('150ms 50ms linear', style({ opacity: 0 })))
|
||||
];
|
51
src/app/context-menu/context-menu-item.directive.ts
Normal file
51
src/app/context-menu/context-menu-item.directive.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
/*!
|
||||
* @license
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* Copyright (C) 2005 - 2018 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Directive, ElementRef, OnDestroy } from '@angular/core';
|
||||
import { FocusableOption, FocusMonitor, FocusOrigin } from '@angular/cdk/a11y';
|
||||
|
||||
@Directive({
|
||||
selector: '[acaContextMenuItem]',
|
||||
})
|
||||
export class ContextMenuItemDirective implements OnDestroy, FocusableOption {
|
||||
constructor(
|
||||
private elementRef: ElementRef,
|
||||
private focusMonitor: FocusMonitor) {
|
||||
|
||||
focusMonitor.monitor(this.getHostElement(), false);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.focusMonitor.stopMonitoring(this.getHostElement());
|
||||
}
|
||||
|
||||
focus(origin: FocusOrigin = 'keyboard'): void {
|
||||
this.focusMonitor.focusVia(this.getHostElement(), origin);
|
||||
}
|
||||
|
||||
private getHostElement(): HTMLElement {
|
||||
return this.elementRef.nativeElement;
|
||||
}
|
||||
}
|
35
src/app/context-menu/context-menu-overlay.ts
Normal file
35
src/app/context-menu/context-menu-overlay.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/*!
|
||||
* @license
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* Copyright (C) 2005 - 2018 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { OverlayRef } from '@angular/cdk/overlay';
|
||||
|
||||
export class ContextMenuOverlayRef {
|
||||
|
||||
constructor(private overlayRef: OverlayRef) { }
|
||||
|
||||
close(): void {
|
||||
this.overlayRef.dispose();
|
||||
}
|
||||
}
|
12
src/app/context-menu/context-menu.component.html
Normal file
12
src/app/context-menu/context-menu.component.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<div mat-menu class="mat-menu-panel" @panelAnimation>
|
||||
<div class="mat-menu-content">
|
||||
<ng-container *ngFor="let entry of actions">
|
||||
<button mat-menu-item
|
||||
acaContextMenuItem
|
||||
(click)="runAction(entry.actions.click)">
|
||||
<mat-icon>{{ entry.icon }}</mat-icon>
|
||||
<span>{{ entry.title | translate }}</span>
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
117
src/app/context-menu/context-menu.component.ts
Normal file
117
src/app/context-menu/context-menu.component.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
/*!
|
||||
* @license
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* Copyright (C) 2005 - 2018 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
Component, ViewEncapsulation, OnInit, OnDestroy, HostListener,
|
||||
ViewChildren, QueryList, AfterViewInit
|
||||
} from '@angular/core';
|
||||
import { trigger } from '@angular/animations';
|
||||
import { FocusKeyManager } from '@angular/cdk/a11y';
|
||||
import { DOWN_ARROW, UP_ARROW } from '@angular/cdk/keycodes';
|
||||
|
||||
import { ExtensionService } from '../extensions/extension.service';
|
||||
import { AppStore, SelectionState } from '../store/states';
|
||||
import { appSelection } from '../store/selectors/app.selectors';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Subject } from 'rxjs/Rx';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { ContextMenuOverlayRef } from './context-menu-overlay';
|
||||
import { ContentActionRef } from '../extensions/action.extensions';
|
||||
import { contextMenuAnimation } from './animations';
|
||||
import { ContextMenuItemDirective } from './context-menu-item.directive';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-context-menu',
|
||||
templateUrl: './context-menu.component.html',
|
||||
host: { 'role': 'menu' },
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
animations: [
|
||||
trigger('panelAnimation', contextMenuAnimation)
|
||||
]
|
||||
})
|
||||
export class ContextMenuComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
private onDestroy$: Subject<boolean> = new Subject<boolean>();
|
||||
private selection: SelectionState;
|
||||
private _keyManager: FocusKeyManager<ContextMenuItemDirective>;
|
||||
actions: Array<ContentActionRef> = [];
|
||||
|
||||
@ViewChildren(ContextMenuItemDirective)
|
||||
private contextMenuItems: QueryList<ContextMenuItemDirective>;
|
||||
|
||||
@HostListener('document:keydown.Escape', ['$event'])
|
||||
handleKeydownEscape(event: KeyboardEvent) {
|
||||
if (event) {
|
||||
this.contextMenuOverlayRef.close();
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('document:keydown', ['$event'])
|
||||
handleKeydownEvent(event: KeyboardEvent) {
|
||||
if (event) {
|
||||
const keyCode = event.keyCode;
|
||||
if (keyCode === UP_ARROW || keyCode === DOWN_ARROW) {
|
||||
this._keyManager.onKeydown(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
private contextMenuOverlayRef: ContextMenuOverlayRef,
|
||||
private extensions: ExtensionService,
|
||||
private store: Store<AppStore>,
|
||||
) { }
|
||||
|
||||
runAction(actionId: string) {
|
||||
const context = {
|
||||
selection: this.selection
|
||||
};
|
||||
|
||||
this.extensions.runActionById(actionId, context);
|
||||
this.contextMenuOverlayRef.close();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.onDestroy$.next(true);
|
||||
this.onDestroy$.complete();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.store
|
||||
.select(appSelection)
|
||||
.pipe(takeUntil(this.onDestroy$))
|
||||
.subscribe(selection => {
|
||||
if (selection.count) {
|
||||
this.selection = selection;
|
||||
this.actions = this.extensions.getAllowedContentContextActions();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this._keyManager = new FocusKeyManager<ContextMenuItemDirective>(this.contextMenuItems);
|
||||
this._keyManager.setFirstItemActive();
|
||||
}
|
||||
}
|
66
src/app/context-menu/context-menu.directive.ts
Normal file
66
src/app/context-menu/context-menu.directive.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
/*!
|
||||
* @license
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* Copyright (C) 2005 - 2018 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Directive, HostListener, Input } from '@angular/core';
|
||||
import { ContextMenuOverlayRef } from './context-menu-overlay';
|
||||
import { ContextMenuService } from './context-menu.service';
|
||||
|
||||
@Directive({
|
||||
selector: '[acaContextActions]'
|
||||
})
|
||||
export class ContextActionsDirective {
|
||||
private overlayRef: ContextMenuOverlayRef = null;
|
||||
|
||||
// tslint:disable-next-line:no-input-rename
|
||||
@Input('acaContextEnable') enabled: boolean;
|
||||
|
||||
@HostListener('window:resize', ['$event'])
|
||||
onResize(event) {
|
||||
if (event && this.overlayRef) {
|
||||
this.overlayRef.close();
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('contextmenu', ['$event'])
|
||||
onContextmenu(event: MouseEvent) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.enabled) {
|
||||
this.render(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private contextMenuService: ContextMenuService) { }
|
||||
|
||||
private render(event: MouseEvent) {
|
||||
this.overlayRef = this.contextMenuService.open({
|
||||
source: event,
|
||||
hasBackdrop: true,
|
||||
panelClass: 'cdk-overlay-pane',
|
||||
});
|
||||
}
|
||||
}
|
73
src/app/context-menu/context-menu.module.ts
Normal file
73
src/app/context-menu/context-menu.module.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
/*!
|
||||
* @license
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* Copyright (C) 2005 - 2018 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { NgModule, ModuleWithProviders } from '@angular/core';
|
||||
import { MatMenuModule, MatListModule, MatIconModule, MatButtonModule } from '@angular/material';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { ContextActionsDirective } from './context-menu.directive';
|
||||
import { ContextMenuService } from './context-menu.service';
|
||||
import { ContextMenuComponent } from './context-menu.component';
|
||||
import { ContextMenuItemDirective } from './context-menu-item.directive';
|
||||
import { CoreModule } from '@alfresco/adf-core';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
MatMenuModule,
|
||||
MatListModule,
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
BrowserModule,
|
||||
CoreModule.forChild()
|
||||
],
|
||||
declarations: [
|
||||
ContextActionsDirective,
|
||||
ContextMenuComponent,
|
||||
ContextMenuItemDirective
|
||||
],
|
||||
exports: [
|
||||
ContextActionsDirective,
|
||||
ContextMenuComponent
|
||||
],
|
||||
entryComponents: [
|
||||
ContextMenuComponent
|
||||
]
|
||||
})
|
||||
export class ContextMenuModule {
|
||||
static forRoot(): ModuleWithProviders {
|
||||
return {
|
||||
ngModule: ContextMenuModule,
|
||||
providers: [
|
||||
ContextMenuService
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
static forChild(): ModuleWithProviders {
|
||||
return {
|
||||
ngModule: ContextMenuModule
|
||||
};
|
||||
}
|
||||
}
|
100
src/app/context-menu/context-menu.service.ts
Normal file
100
src/app/context-menu/context-menu.service.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { Injectable, Injector, ComponentRef, ElementRef } 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';
|
||||
|
||||
@Injectable()
|
||||
export class ContextMenuService {
|
||||
constructor(
|
||||
private injector: Injector,
|
||||
private overlay: Overlay) { }
|
||||
|
||||
open(config: ContextmenuOverlayConfig) {
|
||||
|
||||
const overlay = this.createOverlay(config);
|
||||
|
||||
const overlayRef = new ContextMenuOverlayRef(overlay);
|
||||
|
||||
this.attachDialogContainer(overlay, config, overlayRef);
|
||||
|
||||
overlay.backdropClick().subscribe(() => overlayRef.close());
|
||||
|
||||
// prevent native contextmenu on overlay element if config.hasBackdrop is true
|
||||
(<any>overlay)._backdropElement
|
||||
.addEventListener('contextmenu', () => {
|
||||
event.preventDefault();
|
||||
(<any>overlay)._backdropClick.next(null);
|
||||
}, true);
|
||||
|
||||
return overlayRef;
|
||||
}
|
||||
|
||||
private createOverlay(config: ContextmenuOverlayConfig) {
|
||||
const overlayConfig = this.getOverlayConfig(config);
|
||||
return this.overlay.create(overlayConfig);
|
||||
}
|
||||
|
||||
private attachDialogContainer(overlay: OverlayRef, config: ContextmenuOverlayConfig, contextmenuOverlayRef: ContextMenuOverlayRef) {
|
||||
const injector = this.createInjector(config, contextmenuOverlayRef);
|
||||
|
||||
const containerPortal = new ComponentPortal(ContextMenuComponent, null, injector);
|
||||
const containerRef: ComponentRef<ContextMenuComponent> = overlay.attach(containerPortal);
|
||||
|
||||
return containerRef.instance;
|
||||
}
|
||||
|
||||
private createInjector(config: ContextmenuOverlayConfig, contextmenuOverlayRef: ContextMenuOverlayRef): PortalInjector {
|
||||
const injectionTokens = new WeakMap();
|
||||
|
||||
injectionTokens.set(ContextMenuOverlayRef, contextmenuOverlayRef);
|
||||
|
||||
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 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' }
|
||||
);
|
||||
|
||||
const overlayConfig = new OverlayConfig({
|
||||
hasBackdrop: config.hasBackdrop,
|
||||
backdropClass: config.backdropClass,
|
||||
panelClass: config.panelClass,
|
||||
scrollStrategy: this.overlay.scrollStrategies.close(),
|
||||
positionStrategy
|
||||
});
|
||||
|
||||
return overlayConfig;
|
||||
}
|
||||
}
|
32
src/app/context-menu/interfaces.ts
Normal file
32
src/app/context-menu/interfaces.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/*!
|
||||
* @license
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* Copyright (C) 2005 - 2018 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export interface ContextmenuOverlayConfig {
|
||||
panelClass?: string;
|
||||
hasBackdrop?: boolean;
|
||||
backdropClass?: string;
|
||||
source?: MouseEvent;
|
||||
data?: any;
|
||||
}
|
@@ -46,6 +46,7 @@ export interface ExtensionConfig {
|
||||
navbar?: Array<NavBarGroupRef>;
|
||||
content?: {
|
||||
actions?: Array<ContentActionRef>;
|
||||
contextActions?: Array<ContentActionRef>
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@@ -54,6 +54,7 @@ export class ExtensionService implements RuleContext {
|
||||
|
||||
contentActions: Array<ContentActionRef> = [];
|
||||
viewerActions: Array<ContentActionRef> = [];
|
||||
contentContextmenuActions: Array<ContentActionRef> = [];
|
||||
openWithActions: Array<ContentActionRef> = [];
|
||||
createActions: Array<ContentActionRef> = [];
|
||||
navbar: Array<NavBarGroupRef> = [];
|
||||
@@ -124,6 +125,7 @@ export class ExtensionService implements RuleContext {
|
||||
this.routes = this.loadRoutes(config);
|
||||
this.contentActions = this.loadContentActions(config);
|
||||
this.viewerActions = this.loadViewerActions(config);
|
||||
this.contentContextmenuActions = this.loadContentContextmenuActions(config);
|
||||
this.openWithActions = this.loadViewerOpenWith(config);
|
||||
this.createActions = this.loadCreateActions(config);
|
||||
this.navbar = this.loadNavBar(config);
|
||||
@@ -173,6 +175,14 @@ export class ExtensionService implements RuleContext {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected loadContentContextmenuActions(config: ExtensionConfig): Array<ContentActionRef> {
|
||||
if (config && config.features && config.features.content) {
|
||||
return (config.features.content.contextActions || [])
|
||||
.sort(this.sortByOrder);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
protected loadNavBar(config: ExtensionConfig): any {
|
||||
if (config && config.features) {
|
||||
return (config.features.navbar || [])
|
||||
@@ -335,6 +345,12 @@ export class ExtensionService implements RuleContext {
|
||||
.filter(action => this.filterByRules(action));
|
||||
}
|
||||
|
||||
getAllowedContentContextActions(): Array<ContentActionRef> {
|
||||
return this.contentContextmenuActions
|
||||
.filter(this.filterEnabled)
|
||||
.filter(action => this.filterByRules(action));
|
||||
}
|
||||
|
||||
reduceSeparators(
|
||||
acc: ContentActionRef[],
|
||||
el: ContentActionRef,
|
||||
|
@@ -452,6 +452,175 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"contextActions": [
|
||||
{
|
||||
"id": "app.contextmenu.download",
|
||||
"type": "button",
|
||||
"order": 10,
|
||||
"title": "APP.ACTIONS.DOWNLOAD",
|
||||
"icon": "get_app",
|
||||
"actions": {
|
||||
"click": "DOWNLOAD_NODES"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.toolbar.canDownload"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.preview",
|
||||
"type": "button",
|
||||
"order": 15,
|
||||
"title": "APP.ACTIONS.VIEW",
|
||||
"icon": "open_in_browser",
|
||||
"actions": {
|
||||
"click": "VIEW_FILE"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.toolbar.canViewFile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.editFolder",
|
||||
"type": "button",
|
||||
"order": 20,
|
||||
"title": "APP.ACTIONS.EDIT",
|
||||
"icon": "create",
|
||||
"actions": {
|
||||
"click": "EDIT_FOLDER"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.toolbar.canEditFolder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.share",
|
||||
"type": "button",
|
||||
"title": "APP.ACTIONS.SHARE",
|
||||
"order": 25,
|
||||
"icon": "share",
|
||||
"actions": {
|
||||
"click": "SHARE_NODE"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.selection.file.canShare"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.favorite.add",
|
||||
"type": "button",
|
||||
"title": "APP.ACTIONS.FAVORITE",
|
||||
"order": 30,
|
||||
"icon": "star_border",
|
||||
"actions": {
|
||||
"click": "ADD_FAVORITE"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.toolbar.favorite.canAdd"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.favorite.remove",
|
||||
"type": "button",
|
||||
"title": "APP.ACTIONS.FAVORITE",
|
||||
"order": 30,
|
||||
"icon": "star",
|
||||
"actions": {
|
||||
"click": "REMOVE_FAVORITE"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.toolbar.favorite.canRemove"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.copy",
|
||||
"type": "button",
|
||||
"title": "APP.ACTIONS.COPY",
|
||||
"order": 35,
|
||||
"icon": "content_copy",
|
||||
"actions": {
|
||||
"click": "COPY_NODES"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.toolbar.canCopyNode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.move",
|
||||
"type": "button",
|
||||
"title": "APP.ACTIONS.MOVE",
|
||||
"order": 40,
|
||||
"icon": "library_books",
|
||||
"actions": {
|
||||
"click": "MOVE_NODES"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.selection.canDelete"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.delete",
|
||||
"type": "button",
|
||||
"title": "APP.ACTIONS.DELETE",
|
||||
"order": 45,
|
||||
"icon": "delete",
|
||||
"actions": {
|
||||
"click": "DELETE_NODES"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.selection.canDelete"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.versions",
|
||||
"type": "button",
|
||||
"title": "APP.ACTIONS.VERSIONS",
|
||||
"order": 50,
|
||||
"icon": "history",
|
||||
"actions": {
|
||||
"click": "MANAGE_VERSIONS"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.toolbar.versions"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.permissions",
|
||||
"type": "button",
|
||||
"title": "APP.ACTIONS.PERMISSIONS",
|
||||
"icon": "settings_input_component",
|
||||
"order": 55,
|
||||
"actions": {
|
||||
"click": "MANAGE_PERMISSIONS"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.toolbar.permissions"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.purgeDeletedNodes",
|
||||
"type": "button",
|
||||
"title": "APP.ACTIONS.DELETE_PERMANENT",
|
||||
"icon": "delete_forever",
|
||||
"actions": {
|
||||
"click": "PURGE_DELETED_NODES"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.trashcan.hasSelection"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "app.contextmenu.restoreDeletedNodes",
|
||||
"type": "button",
|
||||
"title": "APP.ACTIONS.RESTORE",
|
||||
"icon": "restore",
|
||||
"actions": {
|
||||
"click": "RESTORE_DELETED_NODES"
|
||||
},
|
||||
"rules": {
|
||||
"visible": "app.trashcan.hasSelection"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"viewer": {
|
||||
|
Reference in New Issue
Block a user