From: Bill Erickson Date: Fri, 22 Nov 2019 17:32:54 +0000 (-0500) Subject: LPXXX editor continued X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=840dd291afbefc8365f17f800cdc01c0a249c0a7;p=working%2FEvergreen.git LPXXX editor continued Signed-off-by: Bill Erickson --- diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/context-menu.component.css b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/context-menu.component.css new file mode 100644 index 0000000000..88b31f275a --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/context-menu.component.css @@ -0,0 +1,20 @@ + +:host >>> .popover { + font-family: 'Lucida Console', Monaco, monospace; + 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: #f8f9fa; /* bootstrap color */ +} + diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/context-menu.component.html b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/context-menu.component.html new file mode 100644 index 0000000000..b9d301bdf9 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/context-menu.component.html @@ -0,0 +1,29 @@ + + +
+ + {{entry.label}} + +
+
+ + + diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/context-menu.component.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/context-menu.component.ts new file mode 100644 index 0000000000..fc255f68dd --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/context-menu.component.ts @@ -0,0 +1,72 @@ +import {Component, Input, Output, OnInit, EventEmitter, Directive, + ViewChild, TemplateRef} from '@angular/core'; +import {NgbPopover} from '@ng-bootstrap/ng-bootstrap'; + +/** + * Right-click context menu component. + * + * No state is maintained (i.e. no current value), events are + * simply emitted as entries are chosen. + * + * + * + */ + +export interface ContextMenuEntry { + value: string; + label: string; +} + +@Component({ + selector: 'eg-context-menu-entries', + templateUrl: './context-menu.component.html', + styleUrls: ['context-menu.component.css'] +}) + +export class ContextMenuComponent implements OnInit { + + @Input() menuEntries: ContextMenuEntry[] = []; + + // Additional CSS classes (space separated) to apply to the entry links + @Input() entryClasses = ''; + + @Output() entrySelected: EventEmitter; + + @ViewChild('menuTemplate', {static: false}) public menuTemplate: TemplateRef; + + constructor() { + this.entrySelected = new EventEmitter(); + } + + ngOnInit() {} + + entryClicked(entry: ContextMenuEntry) { + this.entrySelected.emit(entry); + } +} + + +@Directive({ + selector: '[egContextMenu]', + exportAs: 'egContextMenu' +}) +export class ContextMenuDirective extends NgbPopover { + + @Input() set egContextMenu(menuComp: ContextMenuComponent) { + this.ngbPopover = menuComp.menuTemplate; + } + + // Only one active menu is allowed at a time. + static activeMenu: ContextMenuDirective; + + open() { + super.open(); + if (ContextMenuDirective.activeMenu) { + ContextMenuDirective.activeMenu.close(); + } + ContextMenuDirective.activeMenu = this; + } +} + + diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor-context.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor-context.ts index c0ece9425f..c51b456ffd 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor-context.ts +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor-context.ts @@ -40,6 +40,7 @@ export class MarcEditContext { // NgbPopovers don't always close when we want them to, // specifcially when context-clicking to open other popovers. closePopovers() { + // TODO this.popOvers.forEach(p => p.close()); } diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/fixed-field.component.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/fixed-field.component.ts index 1f6bb8eff1..f0112ede07 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/fixed-field.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/fixed-field.component.ts @@ -1,18 +1,13 @@ import {Component, Input, Output, OnInit, EventEmitter} from '@angular/core'; import {MarcRecord} from './marcrecord'; import {MarcEditContext} from './editor-context'; -import {TagTableService} from './tagtable.service'; +import {TagTableService, ValueLabelPair} 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', @@ -30,7 +25,7 @@ export class FixedFieldComponent implements OnInit { fieldValue: string; fieldMeta: any; fieldLength: number = null; - fieldValues: FixedFieldValue[] = null; + fieldValues: ValueLabelPair[] = null; popOver: NgbPopover; randId = Math.floor(Math.random() * 10000000); diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marc-edit.module.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marc-edit.module.ts index 45e03ed686..6825a48056 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marc-edit.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marc-edit.module.ts @@ -8,6 +8,9 @@ import {FixedFieldComponent} from './fixed-field.component'; import {TagTableService} from './tagtable.service'; import {EditableContentComponent} from './editable-content.component'; +// TODO: consider moving to share +import {ContextMenuDirective, ContextMenuComponent} from './context-menu.component'; + @NgModule({ declarations: [ MarcEditorComponent, @@ -15,7 +18,9 @@ import {EditableContentComponent} from './editable-content.component'; MarcFlatEditorComponent, FixedFieldsEditorComponent, FixedFieldComponent, - EditableContentComponent + EditableContentComponent, + ContextMenuDirective, + ContextMenuComponent ], imports: [ StaffCommonModule diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.ts index 906a9196c9..f03ce49e07 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.ts @@ -41,6 +41,7 @@ export class MarcRichEditorComponent implements OnInit { if (!this.record) { return Promise.resolve(); } return Promise.all([ + this.tagTable.loadTagTable({marcRecordType: this.record.recordType()}), this.tagTable.getFFPosTable(this.record.recordType()), this.tagTable.getFFValueTable(this.record.recordType()) ]).then(_ => this.dataLoaded = true); diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/tagtable.service.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/tagtable.service.ts index d7b470111c..024b1e98ff 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/tagtable.service.ts +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/tagtable.service.ts @@ -1,15 +1,31 @@ import {Injectable, EventEmitter} from '@angular/core'; -import {map} from 'rxjs/operators'; +import {map, tap} from 'rxjs/operators'; import {StoreService} from '@eg/core/store.service'; import {AuthService} from '@eg/core/auth.service'; import {NetService} from '@eg/core/net.service'; import {PcrudService} from '@eg/core/pcrud.service'; import {EventService} from '@eg/core/event.service'; +export interface ValueLabelPair { + value: string; + label: string; +} + +interface TagTableSelector { + marcFormat?: string; + marcRecordType?: string; +} + +const defaultTagTableSelector: TagTableSelector = { + marcFormat : 'marc21', + marcRecordType : 'biblio' +} @Injectable() export class TagTableService { + // Current set of tags in list and map form. + tagMap: {[tag: string]: any} = {}; ffPosMap: {[rtype: string]: any[]} = {}; ffValueMap: {[rtype: string]: any} = {}; @@ -67,6 +83,89 @@ export class TagTableService { return this.ffValueMap[rtype] = table; }); } + + loadTagTable(selector?: TagTableSelector): Promise { + + if (selector) { + if (!selector.marcFormat) { + selector.marcFormat = defaultTagTableSelector.marcFormat; + } + if (!selector.marcRecordType) { + selector.marcRecordType = + defaultTagTableSelector.marcRecordType; + } + } else { + selector = defaultTagTableSelector; + } + + // TODO load from local store cache + + return this.fetchTagTable(selector); + } + + fetchTagTable(selector?: TagTableSelector): Promise { + return this.net.request( + 'open-ils.cat', + 'open-ils.cat.tag_table.all.retrieve.local', + this.auth.token(), selector.marcFormat, selector.marcRecordType + ).pipe(tap(tagData => { + this.tagMap[tagData.tag] = tagData; + })).toPromise(); + } + + getSubfieldCodes(tag: string): ValueLabelPair[] { + if (!tag || !this.tagMap[tag]) { return null; } + + return this.tagMap[tag].subfields + .map(sf => ({ + value: sf.code, + label: `${sf.code}: ${sf.description}` + })) + .sort((a, b) => a.label < b.label ? -1 : 1); + } + + getFieldTags(): ValueLabelPair[] { + return Object.keys(this.tagMap) + .map(tag => ({ + value: tag, + label: `${tag}: ${this.tagMap[tag].name}` + })) + .sort((a, b) => a.label < b.label ? -1 : 1); + } + + getSubfieldValues(tag: string, sfCode: string): ValueLabelPair[] { + if (!tag || !this.tagMap[tag]) { return []; } + + const list: ValueLabelPair[] = []; + + this.tagMap[tag].subfields + .filter(sf => + sf.code === sfCode && sf.hasOwnProperty('value_list')) + .forEach(sf => { + sf.value_list.forEach(value => { + + const label = (value.code == value.description) ? + value.code : `${value.code}: ${value.description}`; + + list.push({value: value.code, label: label}); + }) + }); + + return list.sort((a, b) => a.label < b.label ? -1 : 1); + } + + getIndicatorValues(tag: string, pos: number): ValueLabelPair[] { + if (!tag || !this.tagMap[tag]) { return } + + const value = this.tagMap[tag][`ind${pos}`]; + if (!value) { return; } + + return value.map(tag => ({ + value: value.code, + label: `${value.code}: ${value.description}` + })) + .sort((a, b) => a.label < b.label ? -1 : 1); + } }