src/app/components/graph-import/graph-import.component.ts
Allows importing graphs as text or by uploading files. Supports both JSON and YAML.
AfterViewInit
selector | apollo-graph-import |
styleUrls | ./graph-import.component.scss |
templateUrl | ./graph-import.component.html |
Properties |
|
Methods |
|
Outputs |
Public
constructor(snackBarService: SnackBarService)
|
||||||
Parameters :
|
graphImport | |
Type : EventEmitter
|
|
Public handleTextInput | ||||||||
handleTextInput(textInput: string)
|
||||||||
Parameters :
Returns :
void
|
Public ngAfterViewInit |
ngAfterViewInit()
|
Returns :
void
|
Public onFileInput |
onFileInput()
|
Returns :
void
|
Public onPaste | ||||||
onPaste(event: ClipboardEvent)
|
||||||
Parameters :
Returns :
void
|
Public triggerFileImport |
triggerFileImport()
|
Returns :
void
|
Private Readonly fileInput |
Type : ElementRef<HTMLInputElement>
|
Decorators :
@ViewChild('fileInput')
|
Private Readonly textInput |
Type : ElementRef<HTMLInputElement>
|
Decorators :
@ViewChild('textInput')
|
Public Readonly textInputFormControl |
Default value : new UntypedFormControl('')
|
import { AfterViewInit, Component, ElementRef, EventEmitter, Output, ViewChild } from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import * as YAML from 'yaml';
import D3Graph from 'src/app/model/d3/d3.graph';
import { FOLGraph } from 'src/app/model/domain/fol.graph';
import { SnackBarService } from 'src/app/services/snack-bar.service';
import { terminate } from 'src/app/utils/events';
/**
* Allows importing graphs as text or by uploading files.
* Supports both JSON and YAML.
*/
@Component({
selector: 'apollo-graph-import',
templateUrl: './graph-import.component.html',
styleUrls: ['./graph-import.component.scss'],
})
export class GraphImportComponent implements AfterViewInit {
@Output() public readonly graphImport = new EventEmitter<FOLGraph>();
@ViewChild('textInput') private readonly textInput!: ElementRef<HTMLInputElement>;
@ViewChild('fileInput') private readonly fileInput!: ElementRef<HTMLInputElement>;
public readonly textInputFormControl = new UntypedFormControl('');
public constructor(private readonly snackBarService: SnackBarService) {}
public ngAfterViewInit(): void {
this.textInput.nativeElement.addEventListener('keydown', (event: KeyboardEvent) => {
const inputElement = this.textInput.nativeElement;
if (event.key === 'Tab') {
terminate(event);
const start = inputElement.selectionStart ?? 0;
const end = inputElement.selectionEnd ?? 0;
inputElement.value = inputElement.value.substring(0, start) + '\t' + inputElement.value.substring(end);
inputElement.selectionStart = inputElement.selectionEnd = start + 1;
}
});
}
public onPaste(event: ClipboardEvent): void {
this.handleTextInput(event.clipboardData?.getData('text') ?? '');
}
public triggerFileImport(): void {
this.fileInput.nativeElement.click();
}
public onFileInput(): void {
const files: FileList | null = this.fileInput.nativeElement.files;
if (files === null || files.length < 1) {
this.snackBarService.openSnackBar({ key: 'import.file-error' }, undefined, 10000);
} else {
files[0].text().then((fileContent) => this.handleTextInput(fileContent));
}
this.fileInput.nativeElement.value = '';
}
public handleTextInput(textInput: string = this.textInputFormControl.value): void {
try {
// YAML::parse also works with JSON.
const parsedGraph: FOLGraph = YAML.parse(textInput);
D3Graph.fromDomainGraph(parsedGraph)
.then(() => this.graphImport.emit(parsedGraph))
.catch((error) => {
if (error.key !== undefined) {
this.snackBarService.openSnackBar(error, undefined);
} else {
this.snackBarService.openSnackBar({ key: 'validation.parse-error' }, undefined, 10000);
}
});
} catch (error) {
this.snackBarService.openSnackBar({ key: 'validation.parse-error' }, undefined, 10000);
}
}
}
<div>
<mat-form-field (keyup.enter)="handleTextInput()" style="margin-bottom: -0.25rem" class="import-form-field">
<mat-label>{{ "import.text" | translate }}</mat-label>
<textarea
#textInput
matInput
[formControl]="textInputFormControl"
placeholder="{{ 'import.format-hint' | translate }}"
(paste)="onPaste($event)"
cdkTextareaAutosize
#autosize="cdkTextareaAutosize"
cdkAutosizeMinRows="1"
cdkAutosizeMaxRows="50"
></textarea>
</mat-form-field>
<input #fileInput type="file" id="file" style="display: none" accept=".json,.yml,text/json,application/json,text/yml" (change)="onFileInput()" />
<span>{{ "misc.or" | translate }}</span>
<button mat-raised-button color="primary" (click)="triggerFileImport()">{{ "import.file" | translate }}</button>
</div>
./graph-import.component.scss
:host {
display: block;
}
h1 {
margin: 0;
}
div {
display: flex;
flex-direction: column;
> *:not(:last-child) {
margin-bottom: 1rem;
}
> button {
max-width: fit-content;
}
}