LPXXX editor continued
authorBill Erickson <berickxx@gmail.com>
Mon, 25 Nov 2019 17:17:34 +0000 (12:17 -0500)
committerBill Erickson <berickxx@gmail.com>
Fri, 6 Dec 2019 15:37:03 +0000 (10:37 -0500)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
15 files changed:
Open-ILS/src/eg2/src/app/share/common-widgets.module.ts
Open-ILS/src/eg2/src/app/share/context-menu/context-menu-container.component.css [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/context-menu/context-menu-container.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/context-menu/context-menu-container.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/context-menu/context-menu.component.css [deleted file]
Open-ILS/src/eg2/src/app/share/context-menu/context-menu.component.html [deleted file]
Open-ILS/src/eg2/src/app/share/context-menu/context-menu.component.ts [deleted file]
Open-ILS/src/eg2/src/app/share/context-menu/context-menu.directive.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/context-menu/context-menu.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/context-menu/context-menu.service.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/share/marc-edit/editable-content.component.html
Open-ILS/src/eg2/src/app/staff/share/marc-edit/editable-content.component.ts
Open-ILS/src/eg2/src/app/staff/share/marc-edit/fixed-field.component.html
Open-ILS/src/eg2/src/app/staff/share/marc-edit/fixed-field.component.ts
Open-ILS/src/eg2/src/app/staff/staff.component.html

index 8ba4750..e1f85cd 100644 (file)
@@ -14,7 +14,7 @@ import {DateSelectComponent} from '@eg/share/date-select/date-select.component';
 import {OrgSelectComponent} from '@eg/share/org-select/org-select.component';
 import {DateRangeSelectComponent} from '@eg/share/daterange-select/daterange-select.component';
 import {DateTimeSelectComponent} from '@eg/share/datetime-select/datetime-select.component';
-import {ContextMenuDirective, ContextMenuComponent} from '@eg/share/context-menu/context-menu.component';
+import {ContextMenuModule} from '@eg/share/context-menu/context-menu.module';
 
 
 @NgModule({
@@ -24,16 +24,15 @@ import {ContextMenuDirective, ContextMenuComponent} from '@eg/share/context-menu
     DateSelectComponent,
     OrgSelectComponent,
     DateRangeSelectComponent,
-    DateTimeSelectComponent,
-    ContextMenuDirective,
-    ContextMenuComponent
+    DateTimeSelectComponent
   ],
   imports: [
     CommonModule,
     FormsModule,
     ReactiveFormsModule,
     NgbModule,
-    EgCoreModule
+    EgCoreModule,
+    ContextMenuModule
   ],
   exports: [
     CommonModule,
@@ -46,9 +45,8 @@ import {ContextMenuDirective, ContextMenuComponent} from '@eg/share/context-menu
     OrgSelectComponent,
     DateRangeSelectComponent,
     DateTimeSelectComponent,
-    ContextMenuComponent,
-    ContextMenuDirective
-  ],
+    ContextMenuModule
+  ]
 })
 
 export class CommonWidgetsModule { }
diff --git a/Open-ILS/src/eg2/src/app/share/context-menu/context-menu-container.component.css b/Open-ILS/src/eg2/src/app/share/context-menu/context-menu-container.component.css
new file mode 100644 (file)
index 0000000..3323d2a
--- /dev/null
@@ -0,0 +1,27 @@
+
+.eg-context-menu {
+  /* These fonts were applied specifically for the MARC editor
+   * context menus.  Might want to make these optional. */
+  font-family: 'Lucida Console', Monaco, monospace;
+
+  /* put a hard limit on the popover width */
+  max-width: 550px;
+}
+
+.eg-context-menu .popover-body {
+  max-height: 400px;
+
+  /* Text exceeding the max-height / max-width will results in scrolls.
+   * In most cases, this should not happen. */
+  overflow-y: auto;
+  overflow-x: auto;
+}
+
+.eg-context-menu .popover-body .menu-entry {
+  /* force the menu to expand horizontally to display the text */
+  white-space: nowrap;
+}
+
+.eg-context-menu .popover-body .menu-entry:hover {
+  background-color: #f8f9fa; 
+}
diff --git a/Open-ILS/src/eg2/src/app/share/context-menu/context-menu-container.component.html b/Open-ILS/src/eg2/src/app/share/context-menu/context-menu-container.component.html
new file mode 100644 (file)
index 0000000..d32159a
--- /dev/null
@@ -0,0 +1,9 @@
+
+<ng-template #menuTemplate>
+  <div *ngFor="let entry of menuEntries; first as isFirst" 
+    class="menu-entry {{entryClasses}}">
+    <a 
+      [attr.id]="isFirst ? randId : ''" 
+      (click)="entryClicked(entry)">{{entry.label}}</a>
+  </div>
+</ng-template>
diff --git a/Open-ILS/src/eg2/src/app/share/context-menu/context-menu-container.component.ts b/Open-ILS/src/eg2/src/app/share/context-menu/context-menu-container.component.ts
new file mode 100644 (file)
index 0000000..a5dfdce
--- /dev/null
@@ -0,0 +1,38 @@
+import {Component, Input, Output, EventEmitter, OnInit, ViewChild, 
+    AfterViewInit, TemplateRef, ViewEncapsulation} from '@angular/core';
+import {ContextMenuService, ContextMenu, ContextMenuEntry} from './context-menu.service';
+
+@Component({
+  selector: 'eg-context-menu-container',
+  templateUrl: './context-menu-container.component.html',
+  styleUrls: ['context-menu-container.component.css'],
+  /* Our CSS affects the style of the popover, which may 
+   * be beyond our reach for standard view encapsulation */
+  encapsulation: ViewEncapsulation.None
+})
+
+export class ContextMenuContainerComponent implements OnInit, AfterViewInit {
+
+    menuEntries: ContextMenuEntry[] = [];
+    @ViewChild('menuTemplate', {static: false}) menuTemplate: TemplateRef<any>;
+
+    constructor(private menuService: ContextMenuService) {}
+
+    ngOnInit() {
+
+        this.menuService.showMenuRequest.subscribe(
+            (menu: ContextMenu) => {
+
+            this.menuEntries = menu.entries
+        });
+    }
+
+    ngAfterViewInit() {
+        this.menuService.menuTemplate = this.menuTemplate;
+    }
+
+    entryClicked(entry: ContextMenuEntry) {
+        this.menuService.menuItemSelected.emit(entry);
+    }
+}
+
diff --git a/Open-ILS/src/eg2/src/app/share/context-menu/context-menu.component.css b/Open-ILS/src/eg2/src/app/share/context-menu/context-menu.component.css
deleted file mode 100644 (file)
index 3323d2a..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-
-.eg-context-menu {
-  /* These fonts were applied specifically for the MARC editor
-   * context menus.  Might want to make these optional. */
-  font-family: 'Lucida Console', Monaco, monospace;
-
-  /* put a hard limit on the popover width */
-  max-width: 550px;
-}
-
-.eg-context-menu .popover-body {
-  max-height: 400px;
-
-  /* Text exceeding the max-height / max-width will results in scrolls.
-   * In most cases, this should not happen. */
-  overflow-y: auto;
-  overflow-x: auto;
-}
-
-.eg-context-menu .popover-body .menu-entry {
-  /* force the menu to expand horizontally to display the text */
-  white-space: nowrap;
-}
-
-.eg-context-menu .popover-body .menu-entry:hover {
-  background-color: #f8f9fa; 
-}
diff --git a/Open-ILS/src/eg2/src/app/share/context-menu/context-menu.component.html b/Open-ILS/src/eg2/src/app/share/context-menu/context-menu.component.html
deleted file mode 100644 (file)
index d32159a..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-
-<ng-template #menuTemplate>
-  <div *ngFor="let entry of menuEntries; first as isFirst" 
-    class="menu-entry {{entryClasses}}">
-    <a 
-      [attr.id]="isFirst ? randId : ''" 
-      (click)="entryClicked(entry)">{{entry.label}}</a>
-  </div>
-</ng-template>
diff --git a/Open-ILS/src/eg2/src/app/share/context-menu/context-menu.component.ts b/Open-ILS/src/eg2/src/app/share/context-menu/context-menu.component.ts
deleted file mode 100644 (file)
index df56b96..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-import {Component, Input, Output, AfterViewInit, EventEmitter, Directive, ViewChild,
-    Renderer2, ViewEncapsulation, ComponentFactoryResolver, TemplateRef} from '@angular/core';
-import {NgbPopover} from '@ng-bootstrap/ng-bootstrap';
-
-/**
- * Context menu component.
- *
- * No state is maintained (i.e. there is no current value), events are 
- * simply emitted as entries are chosen.
- *
- * <eg-context-menu #menu 
- *   [menuEntries]="ContextMenuEntry[]"
- *   (entrySelected)="$event === ContextMenuEntry">
- *
- * <input [egContextMenu]="menu" ... />
- */
-
-export interface ContextMenuEntry {
-    value: string;
-    label: string;
-}
-
-@Component({
-  selector: 'eg-context-menu',
-  templateUrl: './context-menu.component.html',
-  styleUrls: ['context-menu.component.css'],
-  /* Our CSS affects the style of the popover, which may 
-   * be beyond our reach for standard view encapsulation */
-  encapsulation: ViewEncapsulation.None
-})
-
-export class ContextMenuComponent implements AfterViewInit {
-
-    @Input() menuEntries: ContextMenuEntry[] = [];
-
-    // Additional CSS classes (space separated) to apply to the entry links
-    @Input() entryClasses = '';
-
-    @Output() entrySelected: EventEmitter<ContextMenuEntry>;
-
-    @ViewChild('menuTemplate', {static: false}) 
-        public menuTemplate: TemplateRef<any>;
-
-    randId = Math.floor(Math.random() * 10000000);
-
-    constructor(private renderer: Renderer2) {
-        this.entrySelected = new EventEmitter<ContextMenuEntry>();
-    }
-
-    ngAfterViewInit() {
-    }
-
-    grabFocus() {
-        // ARG TODO
-        const link = this.renderer.selectRootElement(`[id='${this.randId}']`);
-        setTimeout(() => {
-            console.log('focusing ', link);
-            link.focus();
-        }, 400);
-    }
-
-    entryClicked(entry: ContextMenuEntry) {
-        console.log('emitting ', entry);
-        this.entrySelected.emit(entry);
-    }
-}
-
-@Directive({
-  selector: '[egContextMenu]',
-  exportAs: 'egContextMenu'
-})
-export class ContextMenuDirective extends NgbPopover {
-
-    menuComp: ContextMenuComponent;
-
-    triggers = 'contextmenu'; // TODO TODO
-    popoverClass = 'eg-context-menu';
-
-    @Input() set egContextMenu(menuComp: ContextMenuComponent) {
-        this.menuComp = menuComp;
-    }
-
-    // Only one active menu is allowed at a time.
-    static activeMenu: ContextMenuDirective;
-
-    open() {
-
-        // The popover will automatically close in most cases, but
-        // context menus preventDefault(), which prevents right-click on
-        // other context menus from firing the close operation. Or at
-        // least, that's my assumption.  This fixes it.
-        if (ContextMenuDirective.activeMenu) {
-            ContextMenuDirective.activeMenu.close();
-        }
-
-        if (!this.menuComp.menuEntries ||
-             this.menuComp.menuEntries.length === 0) { 
-             return;
-        }
-
-        this.ngbPopover = this.menuComp.menuTemplate;
-        ContextMenuDirective.activeMenu = this;
-
-        super.open();
-
-        // ARG TODO
-        setTimeout(() => this.menuComp.grabFocus());
-    }
-}
-
-
diff --git a/Open-ILS/src/eg2/src/app/share/context-menu/context-menu.directive.ts b/Open-ILS/src/eg2/src/app/share/context-menu/context-menu.directive.ts
new file mode 100644 (file)
index 0000000..591d9d0
--- /dev/null
@@ -0,0 +1,90 @@
+import {Input, Output, EventEmitter, Directive} from '@angular/core';
+import {NgbPopover} from '@ng-bootstrap/ng-bootstrap';
+import {ContextMenuService, ContextMenu, ContextMenuEntry} from './context-menu.service';
+
+
+/* Import all of this stuff so we can pass it to our parent 
+ * class via its constructor */
+import {
+    Inject, Injector, Renderer2, ElementRef, TemplateRef, ViewContainerRef,
+    ComponentFactoryResolver, NgZone, ChangeDetectorRef, ApplicationRef
+} from '@angular/core';
+import {DOCUMENT} from '@angular/common';
+import {NgbPopoverConfig} from '@ng-bootstrap/ng-bootstrap';
+/* --- */
+
+@Directive({
+  selector: '[egContextMenu]',
+  exportAs: 'egContextMenu'
+})
+export class ContextMenuDirective extends NgbPopover {
+
+    triggers = 'contextmenu';
+    popoverClass = 'eg-context-menu';
+
+    menuEntries: ContextMenuEntry[] = [];
+    menu: ContextMenu;
+
+    @Input() set egContextMenu(menuEntries: ContextMenuEntry[]) {
+        this.menuEntries = menuEntries;
+    }
+
+    @Output() menuItemSelected: EventEmitter<ContextMenuEntry>;
+
+    // Only one active menu is allowed at a time.
+    static activeDirective: ContextMenuDirective;
+    static menuId = 0;
+
+    constructor(
+        p1: ElementRef<HTMLElement>, p2: Renderer2, p3: Injector,
+        p4: ComponentFactoryResolver, p5: ViewContainerRef, p6: NgbPopoverConfig,
+        p7: NgZone, @Inject(DOCUMENT) p8: any, p9: ChangeDetectorRef,
+        p10: ApplicationRef, private menuService: ContextMenuService) {
+
+        // relay injected services to parent
+        super(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
+
+        this.menuItemSelected = new EventEmitter<ContextMenuEntry>();
+
+        this.menuService.menuItemSelected.subscribe(
+            (entry: ContextMenuEntry) => {
+
+            // Only broadcast entry selection to my listeners if I'm 
+            // hosting the menu where the selection occurred.
+
+            if (this.menu && this.menu.id === this.menuService.activeMenu.id) {
+                this.menuItemSelected.emit(entry);
+            }
+        });
+    }
+
+    open() {
+
+        // In certain scenarios (e.g. right-clicking on another context
+        // menu) an open popover will stay open.  Force it closed here.
+        if (ContextMenuDirective.activeDirective) {
+            ContextMenuDirective.activeDirective.close();
+            ContextMenuDirective.activeDirective = null;
+            this.menuService.activeMenu == null;
+        }
+
+        if (!this.menuEntries ||
+             this.menuEntries.length === 0) { 
+             return;
+        }
+
+        this.menu = new ContextMenu();
+        this.menu.id = ContextMenuDirective.menuId++;
+        this.menu.entries = this.menuEntries;
+
+        this.menuService.activeMenu = this.menu;
+        this.menuService.showMenuRequest.emit(this.menu);
+        this.ngbPopover = this.menuService.menuTemplate;
+
+        ContextMenuDirective.activeDirective = this;
+
+        super.open();
+    }
+}
+
+
diff --git a/Open-ILS/src/eg2/src/app/share/context-menu/context-menu.module.ts b/Open-ILS/src/eg2/src/app/share/context-menu/context-menu.module.ts
new file mode 100644 (file)
index 0000000..fb25e61
--- /dev/null
@@ -0,0 +1,24 @@
+import {NgModule} from '@angular/core';
+import {CommonModule} from '@angular/common';
+import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
+import {ContextMenuService} from './context-menu.service';
+import {ContextMenuDirective} from './context-menu.directive';
+import {ContextMenuContainerComponent} from './context-menu-container.component';
+
+@NgModule({
+  declarations: [
+    ContextMenuDirective,
+    ContextMenuContainerComponent
+  ],
+  imports: [
+    CommonModule,
+    NgbModule
+  ],
+  exports: [
+    ContextMenuDirective,
+    ContextMenuContainerComponent
+  ]
+})
+
+export class ContextMenuModule { }
+
diff --git a/Open-ILS/src/eg2/src/app/share/context-menu/context-menu.service.ts b/Open-ILS/src/eg2/src/app/share/context-menu/context-menu.service.ts
new file mode 100644 (file)
index 0000000..bfedf42
--- /dev/null
@@ -0,0 +1,32 @@
+import {Injectable, EventEmitter, TemplateRef} from '@angular/core';
+import {tap} from 'rxjs/operators';
+
+/* Relay requests to/from the context menu directive and its 
+ * template container component */
+
+export interface ContextMenuEntry {
+    value: string;
+    label: string;
+}
+
+export class ContextMenu {
+    id: number;
+    entries: ContextMenuEntry[];
+}
+
+@Injectable({providedIn: 'root'})
+export class ContextMenuService {
+    
+    showMenuRequest: EventEmitter<ContextMenu>;
+    menuItemSelected: EventEmitter<ContextMenuEntry>;
+
+    menuTemplate: TemplateRef<any>;
+    activeMenu: ContextMenu;
+    
+    constructor() {
+        this.showMenuRequest = new EventEmitter<ContextMenu>();
+        this.menuItemSelected = new EventEmitter<ContextMenuEntry>();
+    }
+}
+
+
index ed6c64a..f44f711 100644 (file)
@@ -1,16 +1,12 @@
 
-<eg-context-menu #contextMenu 
-  [menuEntries]="contextMenuEntries()"
-  (entrySelected)="contextMenuChange($event.value)">
-</eg-context-menu>
-
 <ng-container *ngIf="bigText">
   <div contenteditable
     id='{{randId}}' 
     spellcheck="false"
     class="d-inline-block p-1 pt-2 text-dark text-break {{moreClasses}}"
     [attr.tabindex]="fieldText ? -1 : ''"
-    [egContextMenu]="contextMenu"
+    [egContextMenu]="contextMenuEntries()"
+    (menuItemSelected)="contextMenuChange($event.value)"
     (keydown)="inputKeyDown($event)"
     (focus)="focusBigText()"
     (input)="bigTextValueChange()">
@@ -26,7 +22,8 @@
     [maxlength]="maxLength || ''"
     [disabled]="fieldText" 
     [attr.tabindex]="fieldText ? -1 : ''"
-    [egContextMenu]="contextMenu"
+    [egContextMenu]="contextMenuEntries()"
+    (menuItemSelected)="contextMenuChange($event.value)"
     (keydown)="inputKeyDown($event)"
     (focus)="$event.target.select()"
     [ngModel]="getContent()"
index d3602c6..6d1b055 100644 (file)
@@ -3,7 +3,7 @@ import {ElementRef, Component, Input, Output, OnInit, EventEmitter,
 import {filter} from 'rxjs/operators';
 import {MarcRecord, MarcField, MarcSubfield} from './marcrecord';
 import {MarcEditContext, FieldFocusRequest} from './editor-context';
-import {ContextMenuEntry} from '@eg/share/context-menu/context-menu.component';
+import {ContextMenuEntry} from '@eg/share/context-menu/context-menu.service';
 import {TagTableService} from './tagtable.service';
 
 /**
index 3d822f9..52c332b 100644 (file)
@@ -1,11 +1,6 @@
 
 <ng-container *ngIf="fieldMeta">
 
-  <eg-context-menu #contextMenu 
-    [menuEntries]="fieldValues"
-    (entrySelected)="valueChange($event.value)">
-  </eg-context-menu>
-
   <div class="form-inline d-flex">
     <div class="flex-4">
       <span id='label-{{randId}}' class="text-left font-weight-bold">
@@ -18,7 +13,8 @@
       (change)="valueChange()"
       [(ngModel)]="fieldValue" 
       [attr.maxlength]="fieldLength" [attr.size]="fieldLength"
-      [egContextMenu]="contextMenu"
+      [egContextMenu]="fieldValues"
+      (menuItemSelected)="valueChange($event.value)"
       />
   </div>
 </ng-container>
index 3d71ddf..703aaea 100644 (file)
@@ -2,8 +2,6 @@ import {Component, Input, Output, OnInit, EventEmitter} from '@angular/core';
 import {MarcRecord} from './marcrecord';
 import {MarcEditContext} from './editor-context';
 import {TagTableService, ValueLabelPair} from './tagtable.service';
-//import {NgbPopover} from '@ng-bootstrap/ng-bootstrap';
-//import {ContextMenuDirective} from './context-menu.component';
 
 /**
  * MARC Fixed Field Editing Component
@@ -27,7 +25,6 @@ export class FixedFieldComponent implements OnInit {
     fieldMeta: any;
     fieldLength: number = null;
     fieldValues: ValueLabelPair[] = null;
-    //popOver: NgbPopover;
     randId = Math.floor(Math.random() * 10000000);
 
     constructor(private tagTable: TagTableService) {}
index 9002fa8..e7fec07 100644 (file)
@@ -21,3 +21,6 @@
 
 <!-- global toast alerts -->
 <eg-toast></eg-toast>
+
+<!-- context menu DOM insertion point -->
+<eg-context-menu-container></eg-context-menu-container>