LP1936233 Grouped Menu component
authorBill Erickson <berickxx@gmail.com>
Thu, 15 Jul 2021 16:04:27 +0000 (12:04 -0400)
committerBill Erickson <berickxx@gmail.com>
Mon, 24 Oct 2022 15:04:21 +0000 (11:04 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/share/grouped-menu/grouped-menu-entry.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/grouped-menu/grouped-menu.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/grouped-menu/grouped-menu.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/grouped-menu/grouped-menu.module.ts [new file with mode: 0644]

diff --git a/Open-ILS/src/eg2/src/app/share/grouped-menu/grouped-menu-entry.component.ts b/Open-ILS/src/eg2/src/app/share/grouped-menu/grouped-menu-entry.component.ts
new file mode 100644 (file)
index 0000000..79436bd
--- /dev/null
@@ -0,0 +1,41 @@
+import {Component, Input, Output, EventEmitter, OnInit, Host} from '@angular/core';
+import {GroupedMenuComponent, GroupedMenuEntry} from './grouped-menu.component';
+
+@Component({
+  selector: 'eg-grouped-menu-entry',
+  template: '<ng-container></ng-container>'
+})
+
+export class GroupedMenuEntryComponent implements OnInit {
+
+    @Input() label: string;
+    @Input() group: string;
+    @Input() disabled: boolean;
+    @Input() isSeparator: boolean;
+    @Input() newTab: boolean;
+    @Input() routerLink: string;
+    @Input() href: string;
+    @Output() entryClicked: EventEmitter<GroupedMenuEntry> =
+        new EventEmitter<GroupedMenuEntry>();
+
+    menu: GroupedMenuComponent;
+
+    constructor(@Host() menu: GroupedMenuComponent) {
+        this.menu = menu;
+    }
+
+    ngOnInit() {
+        const entry = new GroupedMenuEntry();
+        entry.label = this.label;
+        entry.group = this.group;
+        entry.disabled = this.disabled;
+        entry.newTab = this.newTab;
+        entry.routerLink = this.routerLink;
+        entry.href = this.href;
+        entry.isSeparator = this.isSeparator;
+        entry.entryClicked = this.entryClicked;
+        this.menu.menuEntries.push(entry);
+    }
+}
+
+
diff --git a/Open-ILS/src/eg2/src/app/share/grouped-menu/grouped-menu.component.html b/Open-ILS/src/eg2/src/app/share/grouped-menu/grouped-menu.component.html
new file mode 100644 (file)
index 0000000..bbfdb98
--- /dev/null
@@ -0,0 +1,21 @@
+
+<div ngbDropdown>
+  <button class="btn btn-outline-dark" id="{{domId}}" 
+    ngbDropdownToggle i18n>{{label}}</button>                                
+  <div ngbDropdownMenu>
+    <button class="dropdown-item" *ngFor="let entry of menuEntries"
+      [disabled]="entry.disabled"
+      (click)="performAction(entry)" tabindex="0">
+      <ng-container *ngIf="entry.isGroup">
+        <span class="font-weight-bold font-italic">{{entry.label}}</span>
+      </ng-container>
+      <ng-container *ngIf="entry.isSeparator">
+        <div class="dropdown-divider"></div>
+      </ng-container>
+      <ng-container *ngIf="!entry.isGroup && !entry.isSeparator">
+        <!-- grouped entries are left paddded for group indentation -->        
+        <span [ngClass]="{'ml-2': entry.group}">{{entry.label}}</span>
+      </ng-container>
+    </button>
+  </div>                                                                   
+</div>  
diff --git a/Open-ILS/src/eg2/src/app/share/grouped-menu/grouped-menu.component.ts b/Open-ILS/src/eg2/src/app/share/grouped-menu/grouped-menu.component.ts
new file mode 100644 (file)
index 0000000..9e04f9f
--- /dev/null
@@ -0,0 +1,107 @@
+import {Component, Input, Output, EventEmitter, OnInit, AfterViewInit} from '@angular/core';
+import {Router} from '@angular/router';
+import {Location} from '@angular/common';
+
+export class GroupedMenuEntry {
+    label: string;
+    group: string;
+    disabled: boolean;
+    isSeparator: boolean;
+    isGroup: boolean;
+    routerLink: string;
+    newTab: boolean; // routerLink or href open a new tab
+    href: string;
+    entryClicked: EventEmitter<GroupedMenuEntry>;
+}
+
+@Component({
+  selector: 'eg-grouped-menu',
+  templateUrl: './grouped-menu.component.html'
+})
+
+export class GroupedMenuComponent implements OnInit, AfterViewInit {
+
+    static autoId = 0;
+
+    // Label for dropdown button
+    @Input() label: string;
+
+    @Input() domId = 'grouped-menu-' + GroupedMenuComponent.autoId++;
+
+    menuEntries: GroupedMenuEntry[] = [];
+
+    constructor(
+        private router: Router,
+        private ngLocation: Location
+    ) {}
+
+    ngOnInit() {
+    }
+
+    ngAfterViewInit() {
+        setTimeout(() => this.sortActions());
+    }
+
+    sortActions() {
+        const actions = this.menuEntries;
+
+        const unGrouped = actions.filter(a => !a.group)
+        .sort((a, b) => {
+            return a.label < b.label ? -1 : 1;
+        });
+
+        const grouped = actions.filter(a => Boolean(a.group))
+        .sort((a, b) => {
+            if (a.group === b.group) {
+                return a.label < b.label ? -1 : 1;
+            } else {
+                return a.group < b.group ? -1 : 1;
+            }
+        });
+
+        // Insert group markers for rendering
+        const seen: any = {};
+        const grouped2: any[] = [];
+        grouped.forEach(action => {
+            if (!seen[action.group]) {
+                seen[action.group] = true;
+                const act = new GroupedMenuEntry();
+                act.label = action.group;
+                act.isGroup = true;
+                grouped2.push(act);
+            }
+            grouped2.push(action);
+        });
+
+        this.menuEntries = unGrouped.concat(grouped2);
+    }
+
+    performAction(entry: GroupedMenuEntry) {
+        if (entry.isGroup || entry.isSeparator) { return; }
+
+        // Always emit, even if no one is listening.
+        entry.entryClicked.emit(entry);
+
+        let url;
+        if (entry.href) {
+            url = entry.href;
+        } else if (entry.routerLink) {
+            if (entry.newTab) {
+                url = this.ngLocation.prepareExternalUrl(entry.routerLink);
+            } else {
+                this.router.navigate([entry.routerLink]);
+                return;
+            }
+        }
+
+        if (url) {
+            if (entry.newTab) {
+                window.open(url);
+            } else {
+                location.href = url;
+            }
+            return;
+        }
+    }
+}
+
diff --git a/Open-ILS/src/eg2/src/app/share/grouped-menu/grouped-menu.module.ts b/Open-ILS/src/eg2/src/app/share/grouped-menu/grouped-menu.module.ts
new file mode 100644 (file)
index 0000000..a2dea63
--- /dev/null
@@ -0,0 +1,23 @@
+import {NgModule} from '@angular/core';
+import {CommonModule} from '@angular/common';
+import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
+import {GroupedMenuComponent} from './grouped-menu.component';
+import {GroupedMenuEntryComponent} from './grouped-menu-entry.component';
+
+@NgModule({
+  declarations: [
+    GroupedMenuComponent,
+    GroupedMenuEntryComponent
+  ],
+  imports: [
+    CommonModule,
+    NgbModule
+  ],
+  exports: [
+    GroupedMenuComponent,
+    GroupedMenuEntryComponent
+  ]
+})
+
+export class GroupedMenuModule { }
+