Start adding editor, fix up sidebar styling

This commit is contained in:
2024-06-22 14:19:23 +09:30
parent c9c7878263
commit 9c2531a034
24 changed files with 489 additions and 81 deletions

View File

@@ -0,0 +1,127 @@
import { MediaMatcher } from '@angular/cdk/layout';
import { CommonModule } from '@angular/common';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
HostBinding,
HostListener,
OnDestroy,
output,
signal,
viewChild,
} from '@angular/core';
import { MatListModule } from '@angular/material/list';
import { ProtoMessage } from '../model/proto-message.model';
import { ProtoDefinitionService } from '../proto-definition.service';
import { MatButtonModule } from '@angular/material/button';
@Component({
selector: 'app-proto-definition-selector',
standalone: true,
imports: [CommonModule, MatListModule, MatButtonModule],
template: `
<h2>Protobuf Definitions</h2>
<mat-list>
@for (item of definitionFiles(); track $index) {
<mat-list-item (click)="selectProtoDefinition(item)">{{
item.name
}}</mat-list-item>
}
</mat-list>
<button mat-button (click)="protoSelector.click()">
Select definitions
</button>
<input
#protoSelector
type="file"
(change)="addDefinitionFiles()"
accept=".proto"
/>
<mat-list>
@for (item of selectedDefinition(); track $index) {
<mat-list-item (click)="messageSelected.emit(item)">{{
item.name
}}</mat-list-item>
}
</mat-list>
<!-- TODO: more detail when dragging over so user knows they can drop the file -->
`,
styleUrl: './proto-definition-selector.component.css',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProtoDefinitionSelectorComponent {
protoSelector = viewChild<ElementRef<HTMLInputElement>>('protoSelector');
messageSelected = output<ProtoMessage>();
protected definitionFiles = signal<File[]>([]);
protected selectedDefinition = signal<ProtoMessage[]>([]);
protected isDragging = signal(false);
private currentFiles: string[] = [];
@HostBinding('class.droppable')
get droppable() {
return this.isDragging();
}
constructor(
private protoDefinitionService: ProtoDefinitionService,
private elementRef: ElementRef
) {}
protected async addDefinitionFiles() {
const files = this.protoSelector()?.nativeElement.files;
if (files) {
const definitionFiles = this.definitionFiles();
for (let i = 0; i < files.length; i++) {
if (!this.currentFiles.includes(files[i].name)) {
definitionFiles.push(files[i]);
this.currentFiles.push(files[i].name);
}
}
this.definitionFiles.set(definitionFiles);
}
}
protected async selectProtoDefinition(file: File) {
try {
const protoContents = await file.text();
const messageObjects =
await this.protoDefinitionService.parseProtoDefinition(protoContents);
this.selectedDefinition.set(messageObjects);
} catch (err) {
console.error(err);
alert(
"Failed to parse protobuf definition, please check it's a valid file."
);
}
}
@HostListener('dragover', ['$event'])
onDrag(event: DragEvent) {
event.preventDefault();
}
@HostListener('drop', ['$event'])
onDrop(event: DragEvent) {
event.preventDefault();
const protoSelector = this.protoSelector();
if (protoSelector) {
protoSelector.nativeElement.files = event.dataTransfer?.files ?? null;
}
this.isDragging.set(false);
this.addDefinitionFiles();
}
@HostListener('dragenter')
onDragEnter() {
this.isDragging.set(true);
}
@HostListener('dragleave', ['$event'])
onDragLeave(event: DragEvent) {
if (event.target) this.isDragging.set(false);
}
}