From 347b5e2f15653e7c25ce1b9517e55aec305efccc Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Wed, 4 Dec 2019 14:36:32 -0500 Subject: [PATCH] LPXXX undo/redo Signed-off-by: Bill Erickson --- .../share/marc-edit/editable-content.component.ts | 2 +- .../app/staff/share/marc-edit/editor-context.ts | 233 +++++++++++++-------- 2 files changed, 147 insertions(+), 88 deletions(-) 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 0005a78b94..96d71acf86 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 @@ -391,7 +391,7 @@ export class EditableContentComponent if (evt.ctrlKey) { // ctrl+i / ctrl+d == insert subfield const pos = this.subfield ? this.subfield[2] + 1 : 0; - this.context.insertSubfield(this.field, pos); + this.context.insertStubSubfield(this.field, pos); evt.preventDefault(); } break; 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 90448cbb63..0e48cb7baa 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 @@ -4,6 +4,8 @@ import {NgbPopover} from '@ng-bootstrap/ng-bootstrap'; /* Per-instance MARC editor context. */ +const STUB_DATA_00X = ' '; + export type MARC_EDITABLE_FIELD_TYPE = 'ldr' | 'tag' | 'cfld' | 'ind1' | 'ind2' | 'sfc' | 'sfv'; @@ -27,12 +29,18 @@ export interface UndoRedoAction { // recovery can extract what's needed. field?: MarcField; + // If this is a subfield modification. + subfield?: MarcSubfield; + // Does this action track an addition or deletion. wasAddition?: boolean; - // Reference to position just before the modified position - // in the record, so deletion recovery can correctly position. - precedingPosition?: FieldFocusRequest; + // Position preceding the modified position to mark the position + // of deletion recovery. + prevPosition?: FieldFocusRequest; + + // Where should focus be returned once the undo is processed? + prevFocus?: FieldFocusRequest; } export class MarcEditContext { @@ -70,6 +78,7 @@ export class MarcEditContext { // timeout allows for new components to be built before the // focus request is emitted. setTimeout(() => { + console.log('focusing ', req); this.fieldFocusRequest.emit(req); }); } @@ -100,99 +109,121 @@ export class MarcEditContext { } } - handleStructuralUndoRedo(action: UndoRedoAction, isRedo?: boolean) { + handleStructuralUndoRedo(action: UndoRedoAction) { if (action.wasAddition) { - this.record.deleteFields(action.field); - this.requestFieldFocus(action.precedingPosition); + // Remove the added field and focus the field before it. - } else { - const fieldId = action.field.fieldId; - const prevField = - this.record.getField(action.precedingPosition.fieldId); + if (action.subfield) { - this.record.insertFieldsAfter(prevField, action.field); - - // Recover the original fieldId, which gets re-stamped - // in this.record.insertFields* calls. - action.field.fieldId = fieldId; + const prevPos = action.subfield[2] - 1; + action.field.deleteExactSubfields(action.subfield); + this.focusSubfield(action.field, prevPos); + + } else { + this.record.deleteFields(action.field); + this.requestFieldFocus(action.prevPosition); + } + + } else { + // Re-insert the removed field and focus it - // Focus the newly recovered field. - this.requestFieldFocus(action.position); + if (action.subfield) { + + this.insertSubfield(action.field, action.subfield, true); + this.focusSubfield(action.field, action.subfield[2]); + + } else { + + const fieldId = action.position.fieldId; + const prevField = + this.record.getField(action.prevPosition.fieldId); + + this.record.insertFieldsAfter(prevField, action.field); + + // Recover the original fieldId, which gets re-stamped + // in this.record.insertFields* calls. + action.field.fieldId = fieldId; + + // Focus the newly recovered field. + this.requestFieldFocus(action.position); + } } action.wasAddition = !action.wasAddition; - const moveTo = isRedo ? this.undoStack : this.redoStack; + const moveTo = action.isRedo ? this.undoStack : this.redoStack; moveTo.unshift(action); } - add00X(tag: string) { + trackStructuralUndo(field: MarcField, isAddition: boolean, subfield?: MarcSubfield) { - const field: MarcField = this.record.newField({ - tag : tag, - data : ' ' - }); + const position: FieldFocusRequest = {fieldId: field.fieldId, target: 'tag'}; - this.record.insertOrderedFields(field); + let prevPos: FieldFocusRequest = null; - const focus: FieldFocusRequest = - {fieldId: field.fieldId, target: 'tag'}; + if (subfield) { + position.target = 'sfc'; + position.sfOffset = subfield[2]; - const prevField = this.record.getPreviousField(field.fieldId); + } else { + // No need to track the previous field for subfield mods. - let prevFocus: FieldFocusRequest; - if (prevField) { - prevFocus = {fieldId: prevField.fieldId, target: 'tag'}; + const prevField = this.record.getPreviousField(field.fieldId); + if (prevField) { + prevPos = {fieldId: prevField.fieldId, target: 'tag'}; + } } - this.undoStack.unshift({ - wasAddition: true, + const action = { field: field, - position: focus, - precedingPosition: prevFocus - }); + subfield: subfield, + wasAddition: isAddition, + position: position, + prevPosition: prevPos, + prevFocus: this.lastFocused + }; + + this.undoStack.unshift(action); + } - this.requestFieldFocus(focus); + deleteField(field: MarcField) { + this.trackStructuralUndo(field, false); + + this.focusNextTag(field) || this.focusPreviousTag(field); + + this.record.deleteFields(field); + } + + add00X(tag: string) { + + const field: MarcField = + this.record.newField({tag : tag, data : STUB_DATA_00X}); + + this.record.insertOrderedFields(field); + + this.trackStructuralUndo(field, true); + + this.focusTag(field); } insertReplace008() { - //this.recordChanging(); // delete all of the 008s - [].concat(this.record.field('008', true)).forEach( - f => this.record.deleteFields(f)); - - // add a new 008 - this.record.insertOrderedFields( - this.record.newField({ - tag : '008', - data : this.record.generate008() - }) - ); - } + [].concat(this.record.field('008', true)).forEach(f => { + this.trackStructuralUndo(f, false); + this.record.deleteFields(f); + }); - // Adds a new empty subfield to the provided field at the - // requested subfield position - insertSubfield(field: MarcField, position: number) { - //this.recordChanging(); + const field = this.record.newField({ + tag : '008', data : this.record.generate008()}); - // array index 3 contains that position of the subfield - // in the MARC field. When splicing a new subfield into - // the set, be sure the any that come after the new one - // have their positions bumped to reflect the shift. - field.subfields.forEach( - sf => {if (sf[2] >= position) { sf[2]++; }}); + this.record.insertOrderedFields(field); - const newSf: MarcSubfield = [' ', '', position]; - field.subfields.splice(position, 0, newSf); + this.trackStructuralUndo(field, true); - this.requestFieldFocus({ - fieldId: field.fieldId, - target: 'sfc', - sfOffset: position - }); + this.focusTag(field); } // Add stub field before or after the context field @@ -205,7 +236,6 @@ export class MarcEditContext { } insertField(contextField: MarcField, newField: MarcField, before?: boolean) { - //this.recordChanging(); if (before) { this.record.insertFieldsBefore(contextField, newField); @@ -215,39 +245,71 @@ export class MarcEditContext { this.record.insertFieldsAfter(contextField, newField); this.focusNextTag(contextField); } + + this.trackStructuralUndo(newField, true); } + // Adds a new empty subfield to the provided field at the + // requested subfield position + insertSubfield(field: MarcField, + subfield: MarcSubfield, skipTracking?: boolean) { + const position = subfield[2]; - deleteField(field: MarcField) { - //this.recordChanging(); - this.record.deleteFields(field); - this.focusNextTag(field) || this.focusPreviousTag(field); - } + // array index 3 contains that position of the subfield + // in the MARC field. When splicing a new subfield into + // the set, be sure the any that come after the new one + // have their positions bumped to reflect the shift. + field.subfields.forEach( + sf => {if (sf[2] >= position) { sf[2]++; }}); - deleteSubfield(field: MarcField, subfield: MarcSubfield) { - //this.recordChanging(); - // If subfields remain, focus the previous subfield. - // otherwise focus our tag. - const sfpos = subfield[2] - 1; + field.subfields.splice(position, 0, subfield); - field.deleteExactSubfields(subfield); + if (!skipTracking) { + this.focusSubfield(field, position); + this.trackStructuralUndo(field, true, subfield); + } + } + + insertStubSubfield(field: MarcField, position: number) { + const newSf: MarcSubfield = [' ', '', position]; + this.insertSubfield(field, newSf); + } + + // Focus the requested subfield by its position. If its + // position is less than zero, focus the field's tag instead. + focusSubfield(field: MarcField, position: number) { const focus: FieldFocusRequest = {fieldId: field.fieldId, target: 'tag'}; - if (sfpos >= 0) { - focus.target = 'sfv'; - focus.sfOffset = sfpos; + if (position >= 0) { + // Focus the code instead of the value, because attempting to + // focus an empty (editable) div results in nothing getting focus. + focus.target = 'sfc'; + focus.sfOffset = position; } this.requestFieldFocus(focus); } + deleteSubfield(field: MarcField, subfield: MarcSubfield) { + const sfpos = subfield[2] - 1; // previous subfield + + this.trackStructuralUndo(field, false, subfield); + + field.deleteExactSubfields(subfield); + + this.focusSubfield(field, sfpos); + } + + focusTag(field: MarcField) { + this.requestFieldFocus({fieldId: field.fieldId, target: 'tag'}); + } + // Returns true if the field has a next tag to focus focusNextTag(field: MarcField) { const nextField = this.record.getNextField(field.fieldId); - if (nextField) { - this.requestFieldFocus( - {fieldId: nextField.fieldId, target: 'tag'}); + if (nextField) { + this.focusTag(nextField); return true; } return false; @@ -257,13 +319,10 @@ export class MarcEditContext { focusPreviousTag(field: MarcField): boolean { const prevField = this.record.getPreviousField(field.fieldId); if (prevField) { - this.requestFieldFocus( - {fieldId: prevField.fieldId, target: 'tag'}); + this.focusTag(prevField); return true; } return false; } - - } -- 2.11.0