From: Bill Erickson Date: Thu, 21 Nov 2019 16:31:14 +0000 (-0500) Subject: LPXXX More keyboard event handlers X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=b1477e514a78cbbb3c4d725f6250917286670686;p=working%2FEvergreen.git LPXXX More keyboard event handlers Signed-off-by: Bill Erickson --- diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editable-content.component.html b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editable-content.component.html index bba29d2c68..f63f5aac58 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editable-content.component.html +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editable-content.component.html @@ -5,7 +5,7 @@ class="d-inline-block p-1 text-break {{moreClasses}}" [attr.tabindex]="fieldText ? -1 : ''" (keydown)="inputKeyDown($event)" - (focus)="focusBigText($event)" + (focus)="focusBigText()" (input)="bigTextValueChange()"> diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editable-content.component.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editable-content.component.ts index 3660f37cc0..65529686a7 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editable-content.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editable-content.component.ts @@ -54,24 +54,21 @@ export class EditableContentComponent implements OnInit, AfterViewInit { return; } - this.context.fieldFocusRequest.subscribe((req: FieldFocusRequest) => { - - if (req.fieldId !== this.field.fieldId) { return ; } - if (!this.editInput) { return; } - - if (req.sfOffset === undefined) { - - if (this.fieldType === 'tag') { - // Focus tag input - this.editInput.select(); - } + this.context.fieldFocusRequest.pipe( + filter(req => req.fieldId === this.field.fieldId), + filter(req => req.target === this.fieldType) + ).subscribe((req: FieldFocusRequest) => { + + if (req.sfOffset !== undefined && + req.sfOffset !== Number(this.subfield[2])) { + // subfield focus request, but we are not this subfield return; + } - } - - // TODO: focus editable content input at subfield offset - if (this.fieldType === 'sfv') { - } else if (this.fieldType === 'sfc') { + if (this.bigText) { + this.focusBigText(); + } else { + this.editInput.select(); } }); } @@ -150,8 +147,8 @@ export class EditableContentComponent implements OnInit, AfterViewInit { return Math.max(2, (this.getContent() || '').length) * 1.1; } - focusBigText($event) { - const targetNode = $event.srcElement.firstChild; + focusBigText() { + const targetNode = this.editInput.firstChild; const range = document.createRange(); range.setStart(targetNode, 0); @@ -162,6 +159,7 @@ export class EditableContentComponent implements OnInit, AfterViewInit { selection.addRange(range); } + // Route keydown events to the appropriate handler inputKeyDown(evt: KeyboardEvent) { if (this.fieldType === 'ldr') { @@ -170,67 +168,118 @@ export class EditableContentComponent implements OnInit, AfterViewInit { } switch (evt.key) { + case 'Enter': return this.keyEnter(evt); + case 'Delete': return this.keyDelete(evt); + case 'ArrowDown': return this.keyArrowDown(evt); + case 'ArrowUp': return this.keyArrowUp(evt); + } + } - case 'Enter': - // No part of a marc Record wants bare newlines - evt.preventDefault(); - break; + // ctrl+enter == insert stub field after focused field + // ctrl+shift+enter == insert stub field before focused field + keyEnter(evt: KeyboardEvent) { - case 'Delete': - if (evt.ctrlKey) { // Delete field + if (evt.ctrlKey) { - const field = - this.record.getNextField(this.field.fieldId) || - this.record.getPreviousField(this.field.fieldId); - if (field) { - this.context.requestFieldFocus({fieldId: field.fieldId}); - } + const newField = this.context.record.newField( + {tag: '999', subfields: [[' ', '', '0']]}); - this.record.deleteFields(this.field); - } + if (evt.shiftKey) { + this.context.record.insertFieldsBefore(this.field, newField); + } else { + this.context.record.insertFieldsAfter(this.field, newField); + } - evt.preventDefault(); - break; + this.context.requestFieldFocus( + {fieldId: newField.fieldId, target: 'tag'}); + } - case 'ArrowDown': + evt.preventDefault(); // Bare newlines not allowed. + } - if (evt.ctrlKey) { // Copy field down + // ctrl+delete == delete whole field + // shift+delete == delete subfield + keyDelete(evt: KeyboardEvent) { - this.context.record.insertFieldsAfter( - this.field, - this.context.record.cloneField(this.field) - ); + if (evt.ctrlKey) { + const field = + this.record.getNextField(this.field.fieldId) || + this.record.getPreviousField(this.field.fieldId); - } else { // Jump to next tag + if (field) { + this.context.requestFieldFocus( + {fieldId: field.fieldId, target: 'tag'}); + } - const field = this.record.getNextField(this.field.fieldId); - if (field) { - this.context.requestFieldFocus({fieldId: field.fieldId}); - } - } - evt.preventDefault(); - break; + this.record.deleteFields(this.field); + evt.preventDefault(); - case 'ArrowUp': + } else if (evt.shiftKey && this.subfield) { - if (evt.ctrlKey) { // Copy field up + // If subfields remain, focus the previous subfield. + // otherwise focus our tag. + const sfpos = Number(this.subfield[2]) - 1; - this.context.record.insertFieldsBefore( - this.field, - this.context.record.cloneField(this.field) - ); + this.field.deleteExactSubfields(this.subfield); - } else { // Jump to previous tag + const focus: FieldFocusRequest = + {fieldId: this.field.fieldId, target: 'tag'}; - const field = this.record.getPreviousField(this.field.fieldId); - if (field) { - this.context.requestFieldFocus({fieldId: field.fieldId}); - } - } - evt.preventDefault(); - break; + if (sfpos >= 0) { + focus.target = 'sfv'; + focus.sfOffset = sfpos; + } + + this.context.requestFieldFocus(focus); + + evt.preventDefault(); + } + } + + // down == move focus to tag of next field + // ctrl+down == copy current field down one + keyArrowDown(evt: KeyboardEvent) { + + if (evt.ctrlKey) { // Copy field down + + this.context.record.insertFieldsAfter( + this.field, + this.context.record.cloneField(this.field) + ); + } else { + + const field = this.record.getNextField(this.field.fieldId); + if (field) { + this.context.requestFieldFocus( + {fieldId: field.fieldId, target: 'tag'}); + } } + + evt.preventDefault(); + } + + // up == move focus to tag of previou field + // ctrl+up == copy current field up one + keyArrowUp(evt: KeyboardEvent) { + + if (evt.ctrlKey) { + + this.context.record.insertFieldsBefore( + this.field, + this.context.record.cloneField(this.field) + ); + + } else { + + const field = this.record.getPreviousField(this.field.fieldId); + if (field) { + this.context.requestFieldFocus( + {fieldId: field.fieldId, target: 'tag'}); + } + } + + evt.preventDefault(); } } 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 ca33605f01..f44694812e 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 @@ -6,9 +6,10 @@ import {NgbPopover} from '@ng-bootstrap/ng-bootstrap'; export interface FieldFocusRequest { fieldId: number; - // If specified, the subfield value at the specified - // will be focused. Otherwise, the tag is focused. - sfOffset?: number; + target: 'tag' | 'sfc' | 'sfv'; + + // focus a specific subfield by its offset + sfOffset?: number; } export class MarcEditContext { @@ -43,7 +44,9 @@ export class MarcEditContext { } requestFieldFocus(req: FieldFocusRequest) { - this.fieldFocusRequest.emit(req); + // timeout allows for new components to be built before the + // focus request is emitted. + setTimeout(() => this.fieldFocusRequest.emit(req)); } } diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts index 81705583bb..52445a40e9 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts @@ -20,7 +20,7 @@ export interface MarcField { isControlfield(): boolean; - deleteExactSubfields(subfields: string[][]): number; + deleteExactSubfields(subfields: string[]): number; } export class MarcRecord { @@ -85,11 +85,13 @@ export class MarcRecord { // Give each field an identifier so it may be referenced later. stampFieldIds() { - this.fields.forEach(f => { - if (!f.fieldId) { - f.fieldId = Math.floor(Math.random() * 100000) - } - }); + this.fields.forEach(f => this.stampFieldId(f)); + } + + stampFieldId(field: MarcField) { + if (!field.fieldId) { + field.fieldId = Math.floor(Math.random() * 10000000); + } } insertFieldsBefore(field: MarcField, ...newFields: MarcField[]) { @@ -128,19 +130,26 @@ export class MarcRecord { } } - cloneField(field: MarcField): MarcField { - const newField: any = {tag: field.tag}; + // Turn an field-ish object into a proper MARC.Field + newField(props: any): MarcField { + const field = new MARC21.Field(props); + this.stampFieldId(field); + return field; + } + + cloneField(field: any): MarcField { + const props: any = {tag: field.tag}; if (field.isControlfield()) { - newField.data = field.data; - return new MARC21.Field(newField); - } + props.data = field.data; - newField.ind1 = field.ind1; - newField.ind2 = field.ind2; - newField.subfields = this.cloneSubfields(field.subfields); + } else { + props.ind1 = field.ind1; + props.ind2 = field.ind2; + props.subfields = this.cloneSubfields(field.subfields); + } - return new MARC21.Field(newField); + return this.newField(props); } cloneSubfields(subfields: string[][]): string[][] {