LP1923640 Manage visibility of grid action menu entries
authorBill Erickson <berickxx@gmail.com>
Tue, 13 Apr 2021 17:05:18 +0000 (13:05 -0400)
committerGalen Charlton <gmc@equinoxOLI.org>
Thu, 12 Aug 2021 21:30:53 +0000 (17:30 -0400)
Adds a new Angular Grid configuration menu entry labeled "Manage Actions
Menu", which launches a new dialog which allows staff to show/hide
individual entries in the grid toolbar actions menu / context menu.

The new menu action is disabled when a grid has no toolbar actions.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Erica Rohlfs <erica.rohlfs@equinoxOLI.org>
Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-actions-editor.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-actions-editor.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-actions-menu.component.html
Open-ILS/src/eg2/src/app/share/grid/grid-toolbar.component.html
Open-ILS/src/eg2/src/app/share/grid/grid.module.ts
Open-ILS/src/eg2/src/app/share/grid/grid.ts

diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-actions-editor.component.html b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-actions-editor.component.html
new file mode 100644 (file)
index 0000000..984ef04
--- /dev/null
@@ -0,0 +1,37 @@
+<ng-template #dialogContent>
+  <div class="modal-header bg-info">
+    <h4 class="modal-title" i18n>Grid Menu Configuration</h4>
+    <button type="button" class="close" 
+      i18n-aria-label aria-label="Close" (click)="close()">
+      <span aria-hidden="true">&times;</span>
+    </button>
+  </div>
+  <div class="modal-body">
+    <div class="row">
+      <div class="col-lg-9 eg-grid-header-cell" i18n>Menu Item</div>
+      <div class="col-lg-3 eg-grid-header-cell" i18n>Visible</div>
+    </div>
+    <div class="row pt-1" *ngFor="let action of gridContext.toolbarActions">
+      <ng-container *ngIf="action.isGroup">
+        <div class="col-lg-12 font-weight-bold font-italic">{{action.label}}</div>
+      </ng-container>
+      <ng-container *ngIf="action.isSeparator">
+        <div class="col-lg-12 dropdown-divider"></div>
+      </ng-container>
+      <ng-container *ngIf="!action.isGroup && !action.isSeparator">
+        <div class="col-lg-9">
+          <div [ngClass]="{'pl-2': action.group}"
+            (click)="showHideClicked(action)">{{action.label}}</div>
+        </div>
+        <div class="col-lg-3" (click)="showHideClicked(action)">
+          <span *ngIf="!action.hidden" class="badge badge-success">&#x2713;</span>
+          <span *ngIf="action.hidden" class="badge badge-warning">&#x2717;</span>
+        </div>
+      </ng-container>
+    </div>
+  </div>
+  <div class="modal-footer">
+    <button type="button" class="btn btn-success ml-2" 
+      (click)="close('confirmed')" i18n>Close</button>
+  </div>
+</ng-template>
diff --git a/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-actions-editor.component.ts b/Open-ILS/src/eg2/src/app/share/grid/grid-toolbar-actions-editor.component.ts
new file mode 100644 (file)
index 0000000..1808b61
--- /dev/null
@@ -0,0 +1,32 @@
+import {Component, Input, OnInit, Host} from '@angular/core';
+import {GridToolbarAction, GridContext} from '@eg/share/grid/grid';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+
+/** Allows users to show/hide toolbar action entries */
+
+@Component({
+  selector: 'eg-grid-toolbar-actions-editor',
+  templateUrl: 'grid-toolbar-actions-editor.component.html'
+})
+
+export class GridToolbarActionsEditorComponent extends DialogComponent {
+
+    @Input() gridContext: GridContext;
+
+    showHideClicked(action: GridToolbarAction) {
+        action.hidden = !action.hidden;
+
+        if (!action.group) { return; }
+
+        // When hiding the last entry in a group, hide the group as well.
+
+        const group = this.gridContext.toolbarActions
+            .filter(entry => entry.isGroup && entry.label === action.group)[0];
+
+        const visibles = this.gridContext.toolbarActions
+            .filter(a => a.group === action.group && !a.hidden);
+
+        group.hidden = visibles.length === 0;
+    }
+}
+
index 224128d..97db338 100644 (file)
@@ -1,15 +1,17 @@
-<button class="dropdown-item scrollable-menu" 
-  [disabled]="shouldDisable(action)"
-  (click)="performAction(action)" tabindex="0"
+<ng-container 
   *ngFor="let action of gridContext.toolbarActions; let idx = index">
-  <ng-container *ngIf="action.isGroup">
-    <span class="font-weight-bold font-italic">{{action.label}}</span>
-  </ng-container>
-  <ng-container *ngIf="action.isSeparator">
-    <div class="dropdown-divider"></div>
-  </ng-container>
-  <ng-container *ngIf="!action.isGroup && !action.isSeparator">
-    <!-- grouped entries are left paddded for group indentation -->        
-    <span [ngClass]="{'ml-2': action.group}">{{action.label}}</span>
-  </ng-container>
-</button>
+  <button class="dropdown-item scrollable-menu" *ngIf="!action.hidden"
+    [disabled]="shouldDisable(action)"
+    (click)="performAction(action)" tabindex="0">
+    <ng-container *ngIf="action.isGroup">
+      <span class="font-weight-bold font-italic">{{action.label}}</span>
+    </ng-container>
+    <ng-container *ngIf="action.isSeparator">
+      <div class="dropdown-divider"></div>
+    </ng-container>
+    <ng-container *ngIf="!action.isGroup && !action.isSeparator">
+      <!-- grouped entries are left paddded for group indentation -->        
+      <span [ngClass]="{'ml-2': action.group}">{{action.label}}</span>
+    </ng-container>
+  </button>
+</ng-container>
index 14607b7..656ab99 100644 (file)
@@ -1,3 +1,5 @@
+<eg-grid-toolbar-actions-editor #toolbarActionsEditor [gridContext]="gridContext">
+</eg-grid-toolbar-actions-editor>
 
 <div class="eg-grid-toolbar mb-2">
 
         <span class="material-icons">compare_arrows</span>
         <span class="ml-2" i18n>Manage Column Widths</span>
       </a>
+      <button class="dropdown-item label-with-material-icon"
+        [disabled]="gridContext.toolbarActions.length === 0"
+        (click)="toolbarActionsEditor.open().subscribe()">
+        <span class="material-icons">menu</span>
+        <span class="ml-2" i18n>Manage Actions Menu</span>
+      </button>
       <a class="dropdown-item label-with-material-icon"
         *ngIf="!disableSaveSettings"
         (click)="saveGridConfig()">
index b738ac6..0757fab 100644 (file)
@@ -15,6 +15,7 @@ import {GridColumnConfigComponent} from './grid-column-config.component';
 import {GridColumnWidthComponent} from './grid-column-width.component';
 import {GridPrintComponent} from './grid-print.component';
 import {GridFilterControlComponent} from './grid-filter-control.component';
+import {GridToolbarActionsEditorComponent} from './grid-toolbar-actions-editor.component';
 
 
 @NgModule({
@@ -33,7 +34,8 @@ import {GridFilterControlComponent} from './grid-filter-control.component';
         GridColumnConfigComponent,
         GridColumnWidthComponent,
         GridPrintComponent,
-        GridFilterControlComponent
+        GridFilterControlComponent,
+        GridToolbarActionsEditorComponent
     ],
     imports: [
         EgCommonModule,
index 447d9e6..51d4def 100644 (file)
@@ -490,6 +490,7 @@ export class GridPersistConf {
     version: number;
     limit: number;
     columns: GridColumnPersistConf[];
+    hideToolbarActions: string[];
 }
 
 export class GridContext {
@@ -588,6 +589,7 @@ export class GridContext {
                 if (conf.limit && !this.disablePaging) {
                     this.pager.limit = conf.limit;
                 }
+                this.applyToolbarActionVisibility(conf.hideToolbarActions);
             }
 
             // This is called regardless of the presence of saved
@@ -596,6 +598,29 @@ export class GridContext {
         });
     }
 
+    applyToolbarActionVisibility(hidden: string[]) {
+        if (!hidden || hidden.length === 0) { return; }
+
+        const groups = [];
+        this.toolbarActions.forEach(action => {
+            if (action.isGroup) {
+                groups.push(action);
+            } else if (!action.isSeparator) {
+                action.hidden = hidden.includes(action.label);
+            }
+        });
+
+        // If all actions in a group are hidden, hide the group as well.
+        // Note the group may be marked as hidden in the configuration,
+        // but the addition of new entries within a group should cause
+        // it to be visible again.
+        groups.forEach(group => {
+            const visible = this.toolbarActions
+                .filter(action => action.group === group.label && !action.hidden);
+            group.hidden = visible.length === 0;
+        });
+    }
+
     reload() {
         // Give the UI time to settle before reloading grid data.
         // This can help when data retrieval depends on a value
@@ -1154,6 +1179,13 @@ export class GridContext {
         conf.limit = this.pager.limit;
         conf.columns = this.columnSet.compileSaveObject();
 
+        // Avoid persisting group visibility since that may change
+        // with the addition of new columns.  Always calculate that
+        // in real time.
+        conf.hideToolbarActions = this.toolbarActions
+            .filter(action => !action.isGroup && action.hidden)
+            .map(action => action.label);
+
         return this.store.setItem('eg.grid.' + this.persistKey, conf);
     }
 
@@ -1180,6 +1212,7 @@ export class GridToolbarAction {
     isGroup: boolean; // used for group placeholder entries
     isSeparator: boolean;
     disableOnRows: (rows: any[]) => boolean;
+    hidden?: boolean;
 }
 
 // Buttons are global actions