LP1803787 Grid actions context menu
authorBill Erickson <berickxx@gmail.com>
Mon, 26 Nov 2018 18:21:36 +0000 (18:21 +0000)
committerJane Sandberg <sandbej@linnbenton.edu>
Mon, 17 Jun 2019 21:21:04 +0000 (14:21 -0700)
Display a context menu including the grid actions for selected rows
links when right-clicking on a grid item.

Note the popover displays oriented to the bottom of the item instead of
the mouse click, which is not supported at time of dev.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Jane Sandberg <sandbej@linnbenton.edu>

Open-ILS/src/eg2/src/app/share/grid/grid-body.component.html
Open-ILS/src/eg2/src/app/share/grid/grid-body.component.ts

index a9f35aa..616b6d2 100644 (file)
@@ -1,3 +1,17 @@
+<!-- uses dropdown menu CSS for easy stying, but it's not a dropdown -->
+<ng-template #contextMenu let-gridContext="gridContext">
+  <ng-container *ngFor="let action of gridContext.toolbarActions">
+    <ng-container *ngIf="action.separator">
+      <div class="dropdown-divider"></div>
+    </ng-container>
+    <ng-container *ngIf="!action.separator">
+      <a class="dropdown-item" (click)="performAction(action)">
+        <span class="ml-2">{{action.label}}</span>
+      </a>
+    </ng-container>
+  </ng-container>
+</ng-template>
+
 <!--
   tabindex=1 so the grid body can capture keyboard events.
 -->
         </ng-container>
       </ng-container>
     </div>
+    <!-- contextMenu applied to cells instead of rows so the position
+         of the popover is close to the mouse.  As of writing, no way
+         to position the popover at the mouse -->
     <div role="gridcell" class="eg-grid-cell eg-grid-body-cell"
       [ngStyle]="{flex:col.flex}"
       [ngClass]="{'eg-grid-cell-overflow': context.overflowCells}"
       (dblclick)="onRowDblClick(row)"
       (click)="onRowClick($event, row, idx)"
+      #rowContextMenu="ngbPopover"
+      popoverTitle="Actions for Selected Rows" i18n-popoverTitle
+      (contextmenu)="onRowContextClick($event, row, rowContextMenu)"
+      [ngbPopover]="contextMenu"
+      placement="bottom"
+      triggers="manual"
       *ngFor="let col of context.columnSet.displayColumns()">
 
       <eg-grid-body-cell [context]="context" [row]="row" [column]="col">
index 9c9b190..fb3cb74 100644 (file)
@@ -1,7 +1,8 @@
 import {Component, Input, OnInit, Host} from '@angular/core';
 import {GridContext, GridColumn, GridRowSelector,
-    GridColumnSet, GridDataSource} from './grid';
+    GridToolbarAction, GridColumnSet, GridDataSource} from './grid';
 import {GridComponent} from './grid.component';
+import {NgbPopover} from '@ng-bootstrap/ng-bootstrap';
 
 @Component({
   selector: 'eg-grid-body',
@@ -12,7 +13,13 @@ export class GridBodyComponent implements OnInit {
 
     @Input() context: GridContext;
 
-    constructor(@Host() private grid: GridComponent) {}
+    // Track the context menus so we can manually close them 
+    // when another popover is opened.
+    contextMenus: NgbPopover[];
+
+    constructor(@Host() private grid: GridComponent) {
+        this.contextMenus = [];
+    }
 
     ngOnInit() {}
 
@@ -71,7 +78,7 @@ export class GridBodyComponent implements OnInit {
         }
     }
 
-    onRowClick($event: any, row: any, idx: number) {
+    handleRowClick($event: any, row: any) {
 
         if (this.context.disableSelect) {
             // Avoid any appearance or click behavior when row
@@ -94,7 +101,10 @@ export class GridBodyComponent implements OnInit {
         } else {
             this.context.selectOneRow(index);
         }
+    }
 
+    onRowClick($event: any, row: any, idx: number) {
+        this.handleRowClick($event, row);
         this.grid.onRowClick.emit(row);
     }
 
@@ -102,5 +112,32 @@ export class GridBodyComponent implements OnInit {
         this.grid.onRowActivate.emit(row);
     }
 
+    performAction(action: GridToolbarAction) {
+        action.action(this.context.getSelectedRows());
+    }
+
+    // Apply row selection, track the new menu if needed, 
+    // manually close any existing open menus, open selected menu.
+    onRowContextClick($event, row: any, contextMenu: NgbPopover) {
+        $event.preventDefault(); // prevent browser context menu
+
+        if (this.context.toolbarActions.length === 0) {
+            // No actions to render.
+            return;
+        }
+
+        this.handleRowClick($event, row);
+
+        const existing = this.contextMenus.filter(m => m === contextMenu)[0];
+        if (!existing) {
+            this.contextMenus.push(contextMenu);
+        }
+
+        // Force any previously opened menus to close, which does 
+        // not naturally occur via context-click.
+        this.contextMenus.forEach(m => m.close());
+
+        contextMenu.open({gridContext: this.context});
+    }
 }