LP1852782 Context menu nagivation and FF repairs
authorBill Erickson <berickxx@gmail.com>
Wed, 11 Dec 2019 17:31:15 +0000 (12:31 -0500)
committerBill Erickson <berickxx@gmail.com>
Fri, 21 Feb 2020 16:44:38 +0000 (11:44 -0500)
Allow keyboard navigation of context menus by changing the action links
to buttons.  Teach the menu to close itself once an action has been
selected to cover cases where the popover does not close itself,
specifically on keyboard Enter to select.

Teach the editor to reload the tagtable data when the record type has
changed and refresh all of its child component, since a Type change
impacts all of the tagtable options.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Jane Sandberg <sandbej@linnbenton.edu>
Open-ILS/src/eg2/src/app/share/context-menu/context-menu-container.component.html
Open-ILS/src/eg2/src/app/share/context-menu/context-menu.directive.ts
Open-ILS/src/eg2/src/app/staff/share/marc-edit/editable-content.component.ts
Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.ts
Open-ILS/src/eg2/src/app/staff/share/marc-edit/tagtable.service.ts

index 0d6c0a0..9a30288 100644 (file)
@@ -2,7 +2,9 @@
 <ng-template #menuTemplate>
   <!-- apply (click) to div so user can click anywhere in the row -->
   <div *ngFor="let entry of menuEntries; first as isFirst" 
-   (click)="entryClicked(entry)" class="menu-entry {{entryClasses}}">
-    <a>{{entry.label}}</a>
+    class="menu-entry {{entryClasses}}">
+    <button (click)="entryClicked(entry)" class="btn p-0 m-0">
+      {{entry.label}}
+    </button>
   </div>
 </ng-template>
index 14de769..2b22c4c 100644 (file)
@@ -52,21 +52,38 @@ export class ContextMenuDirective extends NgbPopover {
             // 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) {
+            if (this.activeMenuIsMe()) {
                 this.menuItemSelected.emit(entry);
+
+                // Item selection via keyboard fails to close the menu.
+                // Force it closed.
+                this.cleanup();
             }
         });
     }
 
-    open() {
+    activeMenuIsMe(): boolean {
+        return (
+            this.menu &&
+            this.menuService.activeMenu &&
+            this.menu.id === this.menuService.activeMenu.id
+        );
+    }
 
-        // In certain scenarios (e.g. right-clicking on another context
-        // menu) an open popover will stay open.  Force it closed here.
+    // Close the active menu
+    cleanup() {
         if (ContextMenuDirective.activeDirective) {
             ContextMenuDirective.activeDirective.close();
             ContextMenuDirective.activeDirective = null;
             this.menuService.activeMenu = null;
         }
+    }
+
+    open() {
+
+        // In certain scenarios (e.g. right-clicking on another context
+        // menu) an open popover will stay open.  Force it closed here.
+        this.cleanup();
 
         if (!this.menuEntries ||
              this.menuEntries.length === 0) {
index c6c1ebc..f1afe30 100644 (file)
@@ -188,9 +188,6 @@ export class EditableContentComponent
                 this.maxLength = fieldMeta.length || 1;
             }
         });
-
-        // Fixed field options change when the record type changes.
-        this.context.recordChange.subscribe(_ => this.applyFFOptions());
     }
 
     // These are served dynamically to handle cases where a tag or
@@ -537,6 +534,9 @@ export class EditableContentComponent
 
     contextMenuChange(value: string) {
         this.setContent(value, true);
+
+        // Context menus can steal focus.
+        this.context.requestFieldFocus(this.context.lastFocused);
     }
 }
 
index 1c50c57..337756a 100644 (file)
@@ -1,5 +1,6 @@
 import {Component, Input, Output, OnInit, AfterViewInit, EventEmitter,
     OnDestroy} from '@angular/core';
+import {filter} from 'rxjs/operators';
 import {IdlService} from '@eg/core/idl.service';
 import {OrgService} from '@eg/core/org.service';
 import {TagTableService} from './tagtable.service';
@@ -36,6 +37,10 @@ export class MarcRichEditorComponent implements OnInit {
     ngOnInit() {
         this.init().then(_ =>
             this.context.recordChange.subscribe(__ => this.init()));
+
+        // Changing the Type fixed field means loading new meta-metadata.
+        this.record.fixedFieldChange.pipe(filter(code => code === 'Type'))
+        .subscribe(_ => this.init());
     }
 
     init(): Promise<any> {
@@ -47,7 +52,14 @@ export class MarcRichEditorComponent implements OnInit {
             this.tagTable.loadTagTable({marcRecordType: this.context.recordType}),
             this.tagTable.getFfPosTable(this.record.recordType()),
             this.tagTable.getFfValueTable(this.record.recordType())
-        ]).then(_ => this.dataLoaded = true);
+        ]).then(_ =>
+            // setTimeout forces all of our sub-components to rerender
+            // themselves each time init() is called.  Without this,
+            // changing the record Type would only re-render the fixed
+            // fields editor when data had to be fetched from the
+            // network.  (Sometimes the data is cached).
+            setTimeout(() => this.dataLoaded = true)
+        );
     }
 
     undoCount(): number {
index 13cbd2c..e3571b1 100644 (file)
@@ -135,7 +135,8 @@ export class TagTableService {
             selector = defaultTagTableSelector;
         }
 
-        const cacheKey = 'FFValueTable_' + selector.marcRecordType;
+        const cacheKey =
+            `current_tag_table_${selector.marcFormat}_${selector.marcRecordType}`;
 
         this.tagMap = this.store.getLocalItem(cacheKey);