import {ElementRef, Component, Input, Output, OnInit, EventEmitter,
- AfterViewInit, Renderer2, forwardRef} from '@angular/core';
+ AfterViewInit, Renderer2} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {MarcRecord} from './marcrecord';
import {MarcEditContext} from './editor-context';
@Component({
selector: 'eg-marc-editable-content',
templateUrl: './editable-content.component.html',
- styleUrls: ['./editable-content.component.css'],
- providers: [{
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => EditableContentComponent),
- multi: true
- }]
+ styleUrls: ['./editable-content.component.css']
})
-export class EditableContentComponent
- implements OnInit, AfterViewInit, ControlValueAccessor {
+export class EditableContentComponent implements OnInit, AfterViewInit {
@Input() context: MarcEditContext;
- @Input() readOnly: boolean;
- @Input() content: string;
- @Input() fieldId: number;
+ @Input() field: any; // MARC.Field
+ @Input() fieldType: 'ldr' | 'tag' | 'cfld' | 'ind' | 'sfc' | 'sfv' = null;
- @Input() dataType: 'tag' | 'indicator' | 'subfield' | 'text';
+ // used when the fieldType is ambiguous. E.g. 'ind1'
+ @Input() fieldName: string = null;
+
+ // read-only field text. E.g. 'LDR'
+ @Input() fieldText: string = null;
+
+ // array of subfield code and subfield value
+ @Input() subfield: any; // MARC.Subfield
// space-separated list of additional CSS classes to append
@Input() moreClasses: string;
get record(): MarcRecord { return this.context.record; }
- randId: number;
- editInput: any; // used for bigText
- maxLength: number;
- bigText: boolean;
-
- // Stub functions required by ControlValueAccessor
- propagateChange = (_: any) => {};
- propagateTouch = () => {};
+ bigText = false;
+ randId = Math.floor(Math.random() * 100000);
+ editInput: any; // <input/> or <div contenteditable/>
+ maxLength: number = null;
constructor(private renderer: Renderer2) {
this.randId = Math.floor(Math.random() * 100000);
}
ngOnInit() {
- this.inspectDataType();
+ this.setupDataType();
}
- inspectDataType() {
- switch (this.dataType) {
+ setupDataType() {
+ const content = this.getContent();
+
+ switch (this.fieldType) {
+ case 'ldr':
+ if (content) { this.maxLength = content.length; }
+ break;
case 'tag':
this.maxLength = 3;
// Arrow navigation focuses tags
- this.context.fieldFocusRequest.subscribe(fieldId => {
- if (fieldId === this.fieldId && this.editInput) {
- this.editInput.focus();
- }
- });
+ if (this.field) { // LDR tag does not have a field
+ this.context.fieldFocusRequest.subscribe(fieldId => {
+ if (fieldId === this.field.fieldId && this.editInput) {
+ this.editInput.select();
+ }
+ });
+ }
break;
- case 'indicator':
- case 'subfield':
+ case 'ind':
+ case 'sfc':
this.maxLength = 1;
break;
- // use sfv, etc.
- case 'text':
- this.maxLength = null;
+ case 'sfv':
this.bigText = true;
break;
}
}
+ getContent(): string {
+ if (this.fieldText) { return this.fieldText; } // read-only
+
+ switch (this.fieldType) {
+ case 'ldr': return this.record.leader;
+ case 'cfld': return this.field.data;
+ case 'tag': return this.field.tag;
+ case 'ind': return this.field[this.fieldName];
+ case 'sfc': return this.subfield[0];
+ case 'sfv': return this.subfield[1];
+ }
+ }
+
+ setContent(value: string) {
+
+ if (this.fieldText) { return; } // read-only text
+
+ switch (this.fieldType) {
+ case 'ldr': this.record.leader = value; break;
+ case 'cfld': this.field.data = value; break;
+ case 'tag': this.field.tag = value; break;
+ case 'ind': this.field[this.fieldName] = value; break;
+ case 'sfc': this.subfield[0] = value; break;
+ case 'sfv': this.subfield[1] = value; break;
+ }
+ }
+
+ // Propagate editable div content into our record
+ bigTextValueChange() {
+ this.setContent(this.editInput.innerText);
+ }
+
ngAfterViewInit() {
this.editInput = // numeric id requires [id=...] query selector
this.renderer.selectRootElement(`[id='${this.randId}']`);
+
+ // Initialize the editable div
+ this.editInput.innerText = this.getContent();
}
inputSize(): number {
// give at least 2 chars space and grow with the content
- return Math.max(2, (this.content || '').length) * 1.1;
+ return Math.max(2, (this.getContent() || '').length) * 1.1;
}
- focusDiv($event) {
+ focusBigText($event) {
const targetNode = $event.srcElement.firstChild;
const range = document.createRange();
selection.addRange(range);
}
- valueChange() {
- if (this.bigText && this.editInput) {
- this.content = this.editInput.innerText;
- }
- this.propagateChange(this.content);
- }
-
- writeValue(content: string) {
- if (content === null || content === undefined) {
- content = '';
- }
- this.content = content;
- if (this.bigText && this.editInput) {
- this.editInput.innerText = content;
- }
- }
-
- registerOnChange(fn) {
- this.propagateChange = fn;
- }
-
- 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);
+ const field = this.record.getNextField(this.field.fieldId);
if (field) {
this.context.requestFieldFocus(field.fieldId);
}
}
+ evt.preventDefault();
+ break;
+
+ case 'ArrowUp':
+ if (evt.ctrlKey) {
+ // Copy the field up
+ } else {
+ // Navigate upone field
+ const field = this.record.getPreviousField(this.field.fieldId);
+ if (field) {
+ this.context.requestFieldFocus(field.fieldId);
+ }
+ }
+ evt.preventDefault();
+ break;
+
}
}
}
<!-- LEADER -->
<div class="row pt-0 pb-0 pl-3 form-horizontal">
- <eg-marc-editable-content
- [context]="context"
- dataType="tag"
- i18n-ngModel [ngModel]="'LDR'"
- [readOnly]="true">
+ <eg-marc-editable-content [context]="context" fieldType="tag"
+ fieldText="LDR" i18n-fieldText>
</eg-marc-editable-content>
- <eg-marc-editable-content
- [context]="context"
- dataType="leader"
- [(ngModel)]="record.leader"
- [maxLength]="record.leader.length">
+ <eg-marc-editable-content [context]="context" fieldType="ldr">
</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
- dataType="tag"
- [context]="context"
- [(ngModel)]="field.tag"
- [fieldId]="field.fieldId"
- [maxLength]="3">
+ <eg-marc-editable-content [context]="context" fieldType="tag" [field]="field">
</eg-marc-editable-content>
- <eg-marc-editable-content
- [context]="context"
- dataType="tag"
- [fieldId]="field.fieldId"
- [(ngModel)]="field.data">
+ <eg-marc-editable-content [context]="context" fieldType="cfld" [field]="field">
</eg-marc-editable-content>
</div>
*ngFor="let field of dataFields()">
<!-- TAG -->
- <eg-marc-editable-content
- dataType="tag"
- [context]="context"
- [(ngModel)]="field.tag"
- [fieldId]="field.fieldId"
- [maxLength]="3">
+ <eg-marc-editable-content [context]="context" fieldType="tag" [field]="field">
</eg-marc-editable-content>
<!-- INDICATOR 1 -->
- <eg-marc-editable-content
- [context]="context"
- [(ngModel)]="field.ind1"
- [fieldId]="field.fieldId"
- [maxLength]="1">
+ <eg-marc-editable-content [context]="context" fieldType="ind"
+ [field]="field" fieldName="ind1">
</eg-marc-editable-content>
<!-- INDICATOR 2 -->
- <eg-marc-editable-content
- [context]="context"
- [(ngModel)]="field.ind2"
- [fieldId]="field.fieldId"
- [maxLength]="1">
+ <eg-marc-editable-content [context]="context" fieldType="ind"
+ [field]="field" fieldName="ind2">
</eg-marc-editable-content>
<!-- SUBFIELDS -->
<ng-container *ngFor="let subfield of field.subfields">
<!-- SUBFIELD DECORATOR/DELIMITER -->
- <eg-marc-editable-content
- moreClasses="text-primary border-right-0 bg-transparent"
- [readOnly]="true"
- i18n-ngModel [ngModel]="'‡'">
+ <eg-marc-editable-content fieldText="‡" i18n-fieldText
+ moreClasses="text-primary border-right-0 bg-transparent">
</eg-marc-editable-content>
<!-- SUBFIELD CHARACTER -->
- <eg-marc-editable-content
- moreClasses="text-info border-left-0"
- [(ngModel)]="subfield[0]"
- [context]="context"
- [fieldId]="field.fieldId"
- [maxLength]="1">
+ <eg-marc-editable-content [context]="context" fieldType="sfc"
+ [field]="field" [subfield]="subfield"
+ moreClasses="text-info border-left-0">
</eg-marc-editable-content>
<!-- SUBFIELD VALUE -->
- <eg-marc-editable-content
- [context]="context"
- [bigText]="true"
- [fieldId]="field.fieldId"
- [(ngModel)]="subfield[1]">
+ <eg-marc-editable-content [context]="context" fieldType="sfv"
+ [field]="field" [subfield]="subfield">
</eg-marc-editable-content>
</ng-container>
</div>