LPXXX MARC enriched editor FF popovers
authorBill Erickson <berickxx@gmail.com>
Fri, 15 Nov 2019 17:01:50 +0000 (12:01 -0500)
committerBill Erickson <berickxx@gmail.com>
Fri, 6 Dec 2019 15:36:13 +0000 (10:36 -0500)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts
Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor-context.ts
Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.ts
Open-ILS/src/eg2/src/app/staff/share/marc-edit/fixed-field.component.css [new file with mode: 0644]
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/share/marc-edit/rich-editor.component.html
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 ea49034..db6ba1c 100644 (file)
@@ -52,6 +52,8 @@ export class ComboboxComponent implements ControlValueAccessor, OnInit {
 
     @Input() allowFreeText = false;
 
+    @Input() inputSize: number = null;
+
     // Add a 'required' attribute to the input
     isRequired: boolean;
     @Input() set required(r: boolean) {
index 88447fd..8eccfba 100644 (file)
@@ -1,10 +1,13 @@
 import {EventEmitter} from '@angular/core';
 import {MarcRecord} from './marcrecord';
+import {NgbPopover} from '@ng-bootstrap/ng-bootstrap';
 
 export class MarcEditContext {
 
     recordChange: EventEmitter<MarcRecord>;
 
+    popOvers: NgbPopover[] = [];
+
     private _record: MarcRecord;
     set record(r: MarcRecord) {
         if (r !== this._record) {
@@ -14,12 +17,18 @@ export class MarcEditContext {
     }
 
     get record(): MarcRecord {
-        return this._record; 
+        return this._record;
     }
 
     constructor() {
         this.recordChange = new EventEmitter<MarcRecord>();
     }
 
+    // NgbPopovers don't always close when we want them to,
+    // specifcially when context-clicking.
+    closePopovers() {
+        this.popOvers.forEach(p => p.close());
+    }
+
 }
 
index a4d466e..3294f1e 100644 (file)
@@ -40,8 +40,8 @@ export class MarcEditorComponent implements OnInit {
     }
 
     @Input() set recordXml(xml: string) {
-        if (xml) { 
-            this.fromXml(xml); 
+        if (xml) {
+            this.fromXml(xml);
         }
     }
 
@@ -89,7 +89,7 @@ export class MarcEditorComponent implements OnInit {
 
     ngOnInit() {
         // Default to flat for now since it's all that's supported.
-        //this.editorTab = 'flat';
+        // this.editorTab = 'flat';
 
         this.pcrud.retrieveAll('cbs').subscribe(
             src => this.sources.push({id: +src.id(), label: src.source()}),
diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/fixed-field.component.css b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/fixed-field.component.css
new file mode 100644 (file)
index 0000000..59a5e7e
--- /dev/null
@@ -0,0 +1,21 @@
+
+:host >>> .popover {
+  font-family: monospace;
+  font-size: 120%;
+  max-width: 550px;
+}
+
+:host >>> .popover-body {
+  max-height: 400px;
+  overflow-y: auto;
+  overflow-x: auto;
+}
+
+:host >>> .popover-body .menu-entry {
+  white-space: nowrap;
+}
+
+:host >>> .popover-body .menu-entry:hover {
+  background-color: lightgrey;
+}
+
index 6fd6d50..404d78f 100644 (file)
@@ -1,11 +1,27 @@
 
 <ng-container *ngIf="fieldMeta">
+
+  <ng-template #menuContent>
+    <div *ngFor="let value of fieldValues" class="menu-entry">
+      <a (click)="fieldValue=value.value">
+        <span class="font-monospace">{{value.label}}</span>
+      </a>
+    </div>
+  </ng-template>
+
   <div class="form-inline d-flex border-bottom">
-    <label for='fixed-field-input-{{field}}' class="pr-2 flex-1">{{fieldLabel}}</label>
+    <label for='fixed-field-input-{{field}}' class="pr-2 flex-1">
+      {{fieldLabel}}
+    </label>
+
     <input id='fixed-field-input-{{field}}' type="text" 
       (change)="valueChange($event)"
       class="form-control p-1 flex-1" [(ngModel)]="fieldValue" 
-      [attr.maxlength]="fieldSize" [attr.size]="fieldSize"/>
+      [attr.maxlength]="fieldSize" [attr.size]="fieldSize"
+      [ngbPopover]="menuContent"
+      #p="ngbPopover" triggers="manual"
+      (contextmenu)="contextMenu($event, p)"
+    />
   </div>
 </ng-container>
 
index a7c1fdb..ce27791 100644 (file)
@@ -1,16 +1,22 @@
-import {Component, Input, Output, OnInit, AfterViewInit, EventEmitter,
-    OnDestroy} from '@angular/core';
+import {Component, Input, Output, OnInit, EventEmitter} from '@angular/core';
 import {MarcRecord} from './marcrecord';
 import {MarcEditContext} from './editor-context';
 import {TagTableService} from './tagtable.service';
+import {NgbPopover} from '@ng-bootstrap/ng-bootstrap';
 
 /**
  * MARC Fixed Field Editing Component
  */
 
+interface FixedFieldValue {
+    value: string;
+    label: string;
+}
+
 @Component({
   selector: 'eg-fixed-field',
-  templateUrl: './fixed-field.component.html'
+  templateUrl: './fixed-field.component.html',
+  styleUrls: ['fixed-field.component.css']
 })
 
 export class FixedFieldComponent implements OnInit {
@@ -23,30 +29,33 @@ export class FixedFieldComponent implements OnInit {
 
     fieldValue: string;
     fieldMeta: any;
-    fieldSize = 4;
+    fieldSize: number = null;
+    fieldValues: FixedFieldValue[] = null;
+    popOver: NgbPopover;
 
     constructor(
         private tagTable: TagTableService
     ) {}
 
     ngOnInit() {
-        this.init().then(_ => 
-            this.context.recordChange.subscribe(_ => this.init()));
+        this.init().then(_ =>
+            this.context.recordChange.subscribe(__ => this.init()));
 
     }
 
     init(): Promise<any> {
         if (!this.record) { return Promise.resolve(); }
 
+        this.fieldValues = null;
         return this.tagTable.getFFPosTable(this.record.recordType())
         .then(table => {
 
             // Note the AngJS MARC editor stores the full POS table
-            // for all record types in every copy of the table, hence 
+            // for all record types in every copy of the table, hence
             // the seemingly extraneous check in recordType.
-            this.fieldMeta = table.filter(
-                field => field.fixed_field === this.fieldCode
-                    && field.rec_type === this.record.recordType())[0];
+            this.fieldMeta = table.filter(field =>
+                    field.fixed_field === this.fieldCode
+                 && field.rec_type === this.record.recordType())[0];
 
             if (!this.fieldMeta) {
                 // Not all record types have all field types.
@@ -54,20 +63,46 @@ export class FixedFieldComponent implements OnInit {
             }
 
             this.fieldSize = this.fieldMeta.length || 1;
-            this.fieldValue = 
+            this.fieldValue =
                 this.context.record.extractFixedField(this.fieldCode);
 
-            // Shuffling may occur wht our fixed field value based on
+            // Shuffling may occur with our fixed field as a result of
             // external changes.
-            this.record.fixedFieldChange.subscribe(_ => 
-                this.fieldValue = 
+            this.record.fixedFieldChange.subscribe(_ =>
+                this.fieldValue =
                     this.context.record.extractFixedField(this.fieldCode)
             );
+
+            return this.tagTable.getFFValueTable(this.record.recordType());
+
+        }).then(values => {
+            if (!values || !values[this.fieldCode]) { return; }
+
+            // extract the canned set of possible values for our
+            // fixed field.  Ignore those whose value exceeds the
+            // specified field length.
+            this.fieldValues = values[this.fieldCode]
+                .filter(val => val[0].length <= val[2])
+                .map(val => ({value: val[0], label: `${val[0]}: ${val[1]}`}))
+                .sort((a, b) => a.label < b.label ? -1 : 1);
         });
     }
 
     valueChange(newVal) {
         this.context.record.setFixedField(this.fieldCode, this.fieldValue);
     }
+
+    contextMenu($event, popOver: NgbPopover) {
+        $event.preventDefault();
+        this.context.closePopovers();
+
+        if (this.fieldValues) {
+            if (!this.popOver) {
+                this.popOver = popOver;
+                this.context.popOvers.push(popOver);
+            }
+            this.popOver.open();
+        }
+    }
 }
 
index 5ae2331..588be29 100644 (file)
@@ -1,9 +1,16 @@
 
-<div class="mt-3">
-  <div class="row">
-    <div class="col-lg-10">
-      <eg-fixed-fields-editor [context]="context"></eg-fixed-fields-editor>
+<ng-container *ngIf="!dataLoaded">
+  <eg-progress-inline></eg-progress-inline>
+</ng-container>
+
+<ng-container *ngIf="dataLoaded">
+  <div class="mt-3" class="text-monospace" 
+    (contextmenu)="$event.preventDefault()">
+    <div class="row">
+      <div class="col-lg-10">
+        <eg-fixed-fields-editor [context]="context"></eg-fixed-fields-editor>
+      </div>
     </div>
   </div>
-</div>
+</ng-container>
 
index 9e5528a..9b333ee 100644 (file)
@@ -22,6 +22,8 @@ export class MarcRichEditorComponent implements OnInit {
     @Input() context: MarcEditContext;
     get record(): MarcRecord { return this.context.record; }
 
+    dataLoaded: boolean;
+
     constructor(
         private idl: IdlService,
         private org: OrgService,
@@ -29,17 +31,19 @@ export class MarcRichEditorComponent implements OnInit {
     ) {}
 
     ngOnInit() {
-        this.init().then(_ => 
-            this.context.recordChange.subscribe(_ => this.init()));
+        this.init().then(_ =>
+            this.context.recordChange.subscribe(__ => this.init()));
     }
 
     init(): Promise<any> {
+        this.dataLoaded = false;
+
         if (!this.record) { return Promise.resolve(); }
 
         return Promise.all([
             this.tagTable.getFFPosTable(this.record.recordType()),
             this.tagTable.getFFValueTable(this.record.recordType())
-        ]);
+        ]).then(_ => this.dataLoaded = true);
     }
 }
 
index 956255d..d7b4701 100644 (file)
@@ -12,7 +12,6 @@ export class TagTableService {
 
     ffPosMap: {[rtype: string]: any[]} = {};
     ffValueMap: {[rtype: string]: any} = {};
-    promiseCache: {[ptype: string]: Promise<any>} = {};
 
     constructor(
         private store: StoreService,
@@ -35,21 +34,14 @@ export class TagTableService {
             return Promise.resolve(this.ffPosMap[rtype]);
         }
 
-        if (this.promiseCache[storeKey]) {
-            return this.promiseCache[storeKey];
-        }
-
-        this.promiseCache[storeKey] = this.net.request(
+        return this.net.request(
             'open-ils.fielder', 'open-ils.fielder.cmfpm.atomic',
             {query: {tag: {'!=' : '006'}, rec_type: rtype}}
-        ).toPromise().then(table => {
 
+        ).toPromise().then(table => {
             this.store.setLocalItem(storeKey, table);
-            delete this.promiseCache[storeKey];
             return this.ffPosMap[rtype] = table;
         });
-
-        return this.promiseCache[storeKey];
     }
 
     getFFValueTable(rtype: string): Promise<any> {
@@ -66,20 +58,14 @@ export class TagTableService {
             return Promise.resolve(this.ffValueMap[rtype]);
         }
 
-        if (this.promiseCache[storeKey]) {
-            return this.promiseCache[storeKey];
-        }
-
-        this.promiseCache[storeKey] = this.net.request(
+        return this.net.request(
             'open-ils.cat',
             'open-ils.cat.biblio.fixed_field_values.by_rec_type', rtype
+
         ).toPromise().then(table => {
-            delete this.promiseCache[storeKey];
             this.store.setLocalItem(storeKey, table);
             return this.ffValueMap[rtype] = table;
         });
-
-        return this.promiseCache[storeKey];
     }
 }