id='{{randId}}'
class="d-inline-block p-1 text-break {{moreClasses}}"
[attr.tabindex]="readOnly ? -1 : ''"
+ (keydown)="inputKeyDown($event)"
(focus)="focusDiv($event)"
(blur)="propagateTouch(); valueChange()">
</div>
[maxlength]="maxLength || ''"
[disabled]="readOnly"
[attr.tabindex]="readOnly ? -1 : ''"
+ (keydown)="inputKeyDown($event)"
(focus)="$event.target.select()"
(blur)="propagateTouch()"
[(ngModel)]="content"
}]
})
-export class EditableContentComponent implements OnInit, AfterViewInit, ControlValueAccessor {
+export class EditableContentComponent
+ implements OnInit, AfterViewInit, ControlValueAccessor {
@Input() context: MarcEditContext;
@Input() readOnly: boolean;
@Input() content: string;
- @Input() maxLength: number;
- @Input() bigText: boolean;
+ @Input() fieldId: number;
+
+ @Input() dataType: 'tag' | 'indicator' | 'subfield' | 'text';
// space-separated list of additional CSS classes to append
@Input() moreClasses: string;
get record(): MarcRecord { return this.context.record; }
randId: number;
- editDiv: any; // used for bigText
+ editInput: any; // used for bigText
+ maxLength: number;
+ bigText: boolean;
// Stub functions required by ControlValueAccessor
propagateChange = (_: any) => {};
this.randId = Math.floor(Math.random() * 100000);
}
- ngOnInit() {}
+ ngOnInit() {
+ this.inspectDataType();
+ }
- ngAfterViewInit() {
- if (this.bigText) {
- this.editDiv = // numeric id requires [id=...] query selector
- this.renderer.selectRootElement(`[id='${this.randId}']`);
+ inspectDataType() {
+ switch (this.dataType) {
+
+ case 'tag':
+ this.maxLength = 3;
+
+ // Arrow navigation focuses tags
+ this.context.fieldFocusRequest.subscribe(fieldId => {
+ if (fieldId === this.fieldId && this.editInput) {
+ this.editInput.focus();
+ }
+ });
+ break;
+
+ case 'indicator':
+ case 'subfield':
+ this.maxLength = 1;
+ break;
+
+ // use sfv, etc.
+ case 'text':
+ this.maxLength = null;
+ this.bigText = true;
+ break;
}
}
+ ngAfterViewInit() {
+ this.editInput = // numeric id requires [id=...] query selector
+ this.renderer.selectRootElement(`[id='${this.randId}']`);
+ }
+
inputSize(): number {
// give at least 2 chars space and grow with the content
return Math.max(2, (this.content || '').length) * 1.1;
}
valueChange() {
- if (this.editDiv) {
- this.content = this.editDiv.innerText;
+ if (this.bigText && this.editInput) {
+ this.content = this.editInput.innerText;
}
this.propagateChange(this.content);
}
content = '';
}
this.content = content;
- if (this.editDiv) {
- this.editDiv.innerText = content;
+ if (this.bigText && this.editInput) {
+ this.editInput.innerText = content;
}
}
registerOnTouched(fn) {
this.propagateTouch = fn;
}
+
+ inputKeyDown(evt: KeyboardEvent) {
+ console.log(evt.key);
+
+ switch (evt.key) {
+ case 'ArrowDown':
+ if (evt.ctrlKey) {
+ // Copy the field down
+ } else {
+ // Navigate down one field
+ const field = this.record.getNextField(this.fieldId);
+ if (field) {
+ this.context.requestFieldFocus(field.fieldId);
+ }
+ }
+ }
+ }
}
import {MarcRecord} from './marcrecord';
import {NgbPopover} from '@ng-bootstrap/ng-bootstrap';
+/* Per-instance MARC editor context. */
+
export class MarcEditContext {
recordChange: EventEmitter<MarcRecord>;
+ fieldFocusRequest: EventEmitter<number>;
popOvers: NgbPopover[] = [];
set record(r: MarcRecord) {
if (r !== this._record) {
this._record = r;
+ this._record.stampFieldIds();
this.recordChange.emit(r);
}
}
constructor() {
this.recordChange = new EventEmitter<MarcRecord>();
+ this.fieldFocusRequest = new EventEmitter<number>();
}
// NgbPopovers don't always close when we want them to,
closePopovers() {
this.popOvers.forEach(p => p.close());
}
+
+ requestFieldFocus(id: number) {
+ this.fieldFocusRequest.emit(id);
+ }
}
fixedFieldChange: EventEmitter<string>;
get leader(): string {
- return this.record.leader;
+ return this.record.leader;
}
set leader(l: string) {
}
get fields(): any[] {
- return this.record.fields;
+ return this.record.fields;
}
set fields(f: any[]) {
this.fixedFieldChange.emit(fieldCode);
return response;
}
+
+ // Give each field an identifier so it may be referenced later.
+ stampFieldIds() {
+ this.fields.forEach(f =>
+ f.fieldId = Math.floor(Math.random() * 100000));
+ }
+
+ getField(id: number) {
+ return this.fields.filter(f => f.fieldId === id)[0];
+ }
+
+ getPreviousField(id: number) {
+ for (let idx = 0; idx < this.fields.length; idx++) {
+ if (this.fields[idx].fieldId === id) {
+ return this.fields[idx - 1];
+ }
+ }
+ }
+
+ getNextField(id: number) {
+ for (let idx = 0; idx < this.fields.length; idx++) {
+ if (this.fields[idx].fieldId === id) {
+ return this.fields[idx + 1];
+ }
+ }
+ }
}
<eg-fixed-fields-editor [context]="context"></eg-fixed-fields-editor>
</div>
</div>
+
+ <!-- LEADER -->
<div class="row pt-0 pb-0 pl-3 form-horizontal">
- <eg-marc-editable-content [context]="context" i18n-ngModel [ngModel]="'LDR'"
- [maxLength]="3" [readOnly]="true"></eg-marc-editable-content>
- <eg-marc-editable-content [context]="context" [(ngModel)]="record.leader"
- [maxLength]="record.leader.length"></eg-marc-editable-content>
+ <eg-marc-editable-content
+ [context]="context"
+ dataType="tag"
+ i18n-ngModel [ngModel]="'LDR'"
+ [readOnly]="true">
+ </eg-marc-editable-content>
+
+ <eg-marc-editable-content
+ [context]="context"
+ dataType="leader"
+ [(ngModel)]="record.leader"
+ [maxLength]="record.leader.length">
+ </eg-marc-editable-content>
</div>
+
+ <!-- CONTROL FIELDS -->
<div class="row pt-0 pb-0 pl-3 form-horizontal"
*ngFor="let field of controlFields()">
- <eg-marc-editable-content [context]="context" [(ngModel)]="field.tag"
- [maxLength]="3"></eg-marc-editable-content>
- <eg-marc-editable-content [context]="context" [(ngModel)]="field.data">
+ <eg-marc-editable-content
+ dataType="tag"
+ [context]="context"
+ [(ngModel)]="field.tag"
+ [fieldId]="field.fieldId"
+ [maxLength]="3">
+ </eg-marc-editable-content>
+
+ <eg-marc-editable-content
+ [context]="context"
+ dataType="tag"
+ [fieldId]="field.fieldId"
+ [(ngModel)]="field.data">
</eg-marc-editable-content>
</div>
+
+ <!-- data fields -->
<div class="row pt-0 pb-0 pl-3 form-horizontal"
*ngFor="let field of dataFields()">
- <eg-marc-editable-content [context]="context" [(ngModel)]="field.tag"
- [maxLength]="3"></eg-marc-editable-content>
- <eg-marc-editable-content [context]="context" [(ngModel)]="field.ind1"
- [maxLength]="1"></eg-marc-editable-content>
- <eg-marc-editable-content [context]="context" [(ngModel)]="field.ind2"
- [maxLength]="1"></eg-marc-editable-content>
+ <!-- TAG -->
+ <eg-marc-editable-content
+ dataType="tag"
+ [context]="context"
+ [(ngModel)]="field.tag"
+ [fieldId]="field.fieldId"
+ [maxLength]="3">
+ </eg-marc-editable-content>
+
+ <!-- INDICATOR 1 -->
+ <eg-marc-editable-content
+ [context]="context"
+ [(ngModel)]="field.ind1"
+ [fieldId]="field.fieldId"
+ [maxLength]="1">
+ </eg-marc-editable-content>
+
+ <!-- INDICATOR 2 -->
+ <eg-marc-editable-content
+ [context]="context"
+ [(ngModel)]="field.ind2"
+ [fieldId]="field.fieldId"
+ [maxLength]="1">
+ </eg-marc-editable-content>
+
+ <!-- SUBFIELDS -->
<ng-container *ngFor="let subfield of field.subfields">
- <!-- subfield decorator -->
- <eg-marc-editable-content [readOnly]="true"
+ <!-- SUBFIELD DECORATOR/DELIMITER -->
+ <eg-marc-editable-content
moreClasses="text-primary border-right-0 bg-transparent"
- i18n-ngModel [ngModel]="'‡'"></eg-marc-editable-content>
+ [readOnly]="true"
+ i18n-ngModel [ngModel]="'‡'">
+ </eg-marc-editable-content>
- <!-- subfield character -->
- <eg-marc-editable-content [context]="context" [(ngModel)]="subfield[0]"
+ <!-- SUBFIELD CHARACTER -->
+ <eg-marc-editable-content
moreClasses="text-info border-left-0"
- [maxLength]="1"></eg-marc-editable-content>
+ [(ngModel)]="subfield[0]"
+ [context]="context"
+ [fieldId]="field.fieldId"
+ [maxLength]="1">
+ </eg-marc-editable-content>
- <!-- subfield value -->
- <eg-marc-editable-content [context]="context"
- [bigText]="true" [(ngModel)]="subfield[1]">
+ <!-- SUBFIELD VALUE -->
+ <eg-marc-editable-content
+ [context]="context"
+ [bigText]="true"
+ [fieldId]="field.fieldId"
+ [(ngModel)]="subfield[1]">
</eg-marc-editable-content>
</ng-container>
</div>
dataFields(): any[] {
return this.record.fields.filter(f => !f.isControlfield());
}
-
}