Tidy up editor, file tree, get list field working, allow creating new items
This commit is contained in:
@@ -18,18 +18,13 @@
|
|||||||
</span>
|
</span>
|
||||||
</mat-toolbar>
|
</mat-toolbar>
|
||||||
<mat-sidenav-container autosize>
|
<mat-sidenav-container autosize>
|
||||||
@if(selectedDirectory()) {
|
|
||||||
<mat-sidenav
|
<mat-sidenav
|
||||||
[mode]="isMobile() ? 'over' : 'side'"
|
[mode]="isMobile() ? 'over' : 'side'"
|
||||||
[opened]="leftSideOpen()"
|
[opened]="leftSideOpen()"
|
||||||
(closed)="leftSideOpen.set(false)"
|
(closed)="leftSideOpen.set(false)"
|
||||||
>
|
>
|
||||||
<app-file-tree
|
<app-file-tree (fileSelected)="fileSelected($event)"></app-file-tree>
|
||||||
[workspaceName]="directoryName()"
|
|
||||||
[files]="files()"
|
|
||||||
></app-file-tree>
|
|
||||||
</mat-sidenav>
|
</mat-sidenav>
|
||||||
}
|
|
||||||
<mat-sidenav
|
<mat-sidenav
|
||||||
[opened]="rightSideOpen()"
|
[opened]="rightSideOpen()"
|
||||||
[mode]="isMobile() ? 'over' : 'side'"
|
[mode]="isMobile() ? 'over' : 'side'"
|
||||||
@@ -41,21 +36,8 @@
|
|||||||
></app-proto-definition-selector>
|
></app-proto-definition-selector>
|
||||||
</mat-sidenav>
|
</mat-sidenav>
|
||||||
<mat-sidenav-content>
|
<mat-sidenav-content>
|
||||||
@if (!selectedDirectory()) {
|
@if(selectedMessage()) {
|
||||||
<div
|
|
||||||
style="
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<button mat-button (click)="selectDirectory()" style="margin: auto">
|
|
||||||
Open Folder
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
}@else { @if(selectedMessage()) {
|
|
||||||
<app-editor [selectedMessage]="selectedMessage()!"></app-editor>
|
<app-editor [selectedMessage]="selectedMessage()!"></app-editor>
|
||||||
} }
|
}
|
||||||
</mat-sidenav-content>
|
</mat-sidenav-content>
|
||||||
</mat-sidenav-container>
|
</mat-sidenav-container>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
|
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component, computed, signal } from '@angular/core';
|
import { Component, signal } from '@angular/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
@@ -8,17 +8,14 @@ import { MatSidenavModule } from '@angular/material/sidenav';
|
|||||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||||
import { MatTreeModule } from '@angular/material/tree';
|
import { MatTreeModule } from '@angular/material/tree';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
import { message, open } from '@tauri-apps/api/dialog';
|
|
||||||
import { UnlistenFn, listen } from '@tauri-apps/api/event';
|
|
||||||
import { FileEntry, readDir } from '@tauri-apps/api/fs';
|
|
||||||
import { EditorComponent } from './editor/editor.component';
|
import { EditorComponent } from './editor/editor.component';
|
||||||
import {
|
import {
|
||||||
FileOrFolder,
|
FileOrFolder,
|
||||||
FileTreeComponent,
|
FileTreeComponent,
|
||||||
} from './file-tree/file-tree.component';
|
} from './file-tree/file-tree.component';
|
||||||
import { OpenFolderMessage } from './messages/openfolder.message';
|
|
||||||
import { ProtoMessage } from './model/proto-message.model';
|
import { ProtoMessage } from './model/proto-message.model';
|
||||||
import { ProtoDefinitionSelectorComponent } from './proto-definition-selector/proto-definition-selector.component';
|
import { ProtoDefinitionSelectorComponent } from './proto-definition-selector/proto-definition-selector.component';
|
||||||
|
import { readTextFile } from '@tauri-apps/api/fs';
|
||||||
const mobileBreakpoints = [Breakpoints.Handset, Breakpoints.TabletPortrait];
|
const mobileBreakpoints = [Breakpoints.Handset, Breakpoints.TabletPortrait];
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -40,27 +37,15 @@ const mobileBreakpoints = [Breakpoints.Handset, Breakpoints.TabletPortrait];
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
protected selectedDirectory = signal<string | null>(null);
|
protected selectedFileContents = signal<string | undefined>(undefined);
|
||||||
protected files = signal<FileOrFolder[]>([]);
|
|
||||||
protected selectedMessage = signal<ProtoMessage | undefined>(undefined);
|
protected selectedMessage = signal<ProtoMessage | undefined>(undefined);
|
||||||
protected rightSideOpen = signal(true);
|
protected rightSideOpen = signal(true);
|
||||||
protected leftSideOpen = signal(true);
|
protected leftSideOpen = signal(true);
|
||||||
|
|
||||||
private unlisten?: UnlistenFn;
|
|
||||||
|
|
||||||
protected isMobile = signal(
|
protected isMobile = signal(
|
||||||
this.breakpointObserver.isMatched(mobileBreakpoints)
|
this.breakpointObserver.isMatched(mobileBreakpoints)
|
||||||
);
|
);
|
||||||
|
|
||||||
protected directoryName = computed(() => {
|
|
||||||
const directory = this.selectedDirectory();
|
|
||||||
if (directory) {
|
|
||||||
const directorySplit = directory.split('/');
|
|
||||||
return directorySplit[directorySplit.length - 1];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
constructor(private breakpointObserver: BreakpointObserver) {
|
constructor(private breakpointObserver: BreakpointObserver) {
|
||||||
breakpointObserver
|
breakpointObserver
|
||||||
.observe(mobileBreakpoints)
|
.observe(mobileBreakpoints)
|
||||||
@@ -68,60 +53,18 @@ export class AppComponent {
|
|||||||
.subscribe((matches) => this.isMobile.set(matches.matches));
|
.subscribe((matches) => this.isMobile.set(matches.matches));
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
|
||||||
this.unlisten = await listen(
|
|
||||||
'openfolder',
|
|
||||||
async (event: OpenFolderMessage) => {
|
|
||||||
await this.selectDirectory();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnDestroy() {
|
|
||||||
if (this.unlisten) {
|
|
||||||
this.unlisten();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async selectDirectory() {
|
|
||||||
const selectedDirectory = await open({
|
|
||||||
directory: true,
|
|
||||||
multiple: false,
|
|
||||||
});
|
|
||||||
if (Array.isArray(selectedDirectory)) {
|
|
||||||
message('Only a single folder can be selected at a time');
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
this.selectedDirectory.set(selectedDirectory);
|
|
||||||
}
|
|
||||||
if (selectedDirectory) {
|
|
||||||
const entries = await readDir(selectedDirectory, {
|
|
||||||
recursive: true,
|
|
||||||
});
|
|
||||||
const splitNumbers = /(\d)+|(\D)+/;
|
|
||||||
this.files.set(
|
|
||||||
entries
|
|
||||||
.sort(this.sortFiles(splitNumbers))
|
|
||||||
.map((entry) => this.mapEntry(entry, splitNumbers))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private mapEntry(entry: FileEntry, splitNumbers: RegExp): FileOrFolder {
|
|
||||||
return {
|
|
||||||
isDirectory: entry.children != null,
|
|
||||||
name: entry.name || '',
|
|
||||||
children: entry.children
|
|
||||||
?.sort(this.sortFiles(splitNumbers))
|
|
||||||
.map((entry) => this.mapEntry(entry, splitNumbers)),
|
|
||||||
path: entry.path,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private sortFiles = (splitNumbers: RegExp) => (a: FileEntry, b: FileEntry) =>
|
|
||||||
a.name?.localeCompare(b.name ?? '') ?? 0;
|
|
||||||
|
|
||||||
protected selectMessage(message: ProtoMessage) {
|
protected selectMessage(message: ProtoMessage) {
|
||||||
this.selectedMessage.set(message);
|
this.selectedMessage.set(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async fileSelected(file: FileOrFolder) {
|
||||||
|
try {
|
||||||
|
this.selectedFileContents.set(await readTextFile(file.path));
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
alert(
|
||||||
|
'Failed to read selected file, please ensure you have appropriate permissions and try again.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
<h2>{{ selectedMessage().name }}</h2>
|
||||||
<div>
|
<div>
|
||||||
@for (item of selectedMessage().values; track $index) {
|
@for (item of selectedMessage().values; track $index) {
|
||||||
<app-proto-field
|
<app-proto-field
|
||||||
|
[label]="item.name"
|
||||||
[configuration]="item.configuration"
|
[configuration]="item.configuration"
|
||||||
[(value)]="values()[$index]"
|
[(value)]="values()[$index]"
|
||||||
></app-proto-field>
|
></app-proto-field>
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
:host {
|
||||||
|
padding: var(--mat-sidenav-container-shape);
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common';
|
|||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
|
computed,
|
||||||
effect,
|
effect,
|
||||||
input,
|
input,
|
||||||
signal,
|
signal,
|
||||||
@@ -22,8 +23,12 @@ export class EditorComponent {
|
|||||||
// TODO: This needs to be reworked so we have a local property and implement some kind of auto-save
|
// TODO: This needs to be reworked so we have a local property and implement some kind of auto-save
|
||||||
fileContents = input<string>();
|
fileContents = input<string>();
|
||||||
selectedMessage = input.required<ProtoMessage>();
|
selectedMessage = input.required<ProtoMessage>();
|
||||||
// TODO: This needs to start with the parsed file contents, and get updated when the code value changes
|
|
||||||
protected values = signal([]);
|
protected values = computed(() => {
|
||||||
|
const message = this.selectedMessage();
|
||||||
|
return message.values.map((value) => null);
|
||||||
|
});
|
||||||
|
|
||||||
private code = viewChild<ElementRef<HTMLElement>>('code');
|
private code = viewChild<ElementRef<HTMLElement>>('code');
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
<h2>{{ workspaceName() }}</h2>
|
<h2>@if(workspaceName()) {workspaceName()} @else { No Worspace Selected}</h2>
|
||||||
|
@if(!selectedDirectory()) {
|
||||||
|
<div>
|
||||||
|
<button mat-button (click)="selectDirectory()" style="margin: auto">
|
||||||
|
Open Folder
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}@else {
|
||||||
<mat-tree [dataSource]="dataSource()" [treeControl]="treeControl">
|
<mat-tree [dataSource]="dataSource()" [treeControl]="treeControl">
|
||||||
<mat-tree-node
|
<mat-tree-node
|
||||||
*matTreeNodeDef="let node"
|
*matTreeNodeDef="let node"
|
||||||
@@ -24,3 +31,4 @@
|
|||||||
{{ node.file.name }}
|
{{ node.file.name }}
|
||||||
</mat-tree-node>
|
</mat-tree-node>
|
||||||
</mat-tree>
|
</mat-tree>
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import { FlatTreeControl } from '@angular/cdk/tree';
|
import { FlatTreeControl } from '@angular/cdk/tree';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component, computed, input, output } from '@angular/core';
|
import {
|
||||||
|
Component,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
computed,
|
||||||
|
output,
|
||||||
|
signal,
|
||||||
|
} from '@angular/core';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import {
|
import {
|
||||||
@@ -8,6 +15,10 @@ import {
|
|||||||
MatTreeFlattener,
|
MatTreeFlattener,
|
||||||
MatTreeModule,
|
MatTreeModule,
|
||||||
} from '@angular/material/tree';
|
} from '@angular/material/tree';
|
||||||
|
import { message, open } from '@tauri-apps/api/dialog';
|
||||||
|
import { UnlistenFn, listen } from '@tauri-apps/api/event';
|
||||||
|
import { FileEntry, readDir } from '@tauri-apps/api/fs';
|
||||||
|
import { OpenFolderMessage } from '../messages/openfolder.message';
|
||||||
|
|
||||||
export interface FileOrFolder {
|
export interface FileOrFolder {
|
||||||
isDirectory: boolean;
|
isDirectory: boolean;
|
||||||
@@ -29,12 +40,10 @@ interface FileNode {
|
|||||||
templateUrl: './file-tree.component.html',
|
templateUrl: './file-tree.component.html',
|
||||||
styleUrl: './file-tree.component.scss',
|
styleUrl: './file-tree.component.scss',
|
||||||
})
|
})
|
||||||
export class FileTreeComponent {
|
export class FileTreeComponent implements OnInit, OnDestroy {
|
||||||
workspaceName = input<string | null>();
|
|
||||||
files = input<FileOrFolder[]>([]);
|
|
||||||
|
|
||||||
fileSelected = output<FileOrFolder>();
|
fileSelected = output<FileOrFolder>();
|
||||||
|
|
||||||
|
// File tree
|
||||||
protected hasChild = (_: number, node: FileNode) => node.expandable;
|
protected hasChild = (_: number, node: FileNode) => node.expandable;
|
||||||
|
|
||||||
private _transformer = (node: FileOrFolder, level: number) => ({
|
private _transformer = (node: FileOrFolder, level: number) => ({
|
||||||
@@ -64,4 +73,71 @@ export class FileTreeComponent {
|
|||||||
dataSource.data = this.files();
|
dataSource.data = this.files();
|
||||||
return dataSource;
|
return dataSource;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Folder selection
|
||||||
|
protected selectedDirectory = signal<string | null>(null);
|
||||||
|
protected files = signal<FileOrFolder[]>([]);
|
||||||
|
protected workspaceName = computed(() => {
|
||||||
|
const directory = this.selectedDirectory();
|
||||||
|
if (directory) {
|
||||||
|
const directorySplit = directory.split('/');
|
||||||
|
return directorySplit[directorySplit.length - 1];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
private unlisten?: UnlistenFn;
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.unlisten = await listen(
|
||||||
|
'openfolder',
|
||||||
|
async (event: OpenFolderMessage) => {
|
||||||
|
await this.selectDirectory();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async ngOnDestroy() {
|
||||||
|
if (this.unlisten) {
|
||||||
|
this.unlisten();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async selectDirectory() {
|
||||||
|
const selectedDirectory = await open({
|
||||||
|
directory: true,
|
||||||
|
multiple: false,
|
||||||
|
});
|
||||||
|
if (Array.isArray(selectedDirectory)) {
|
||||||
|
message('Only a single folder can be selected at a time');
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this.selectedDirectory.set(selectedDirectory);
|
||||||
|
}
|
||||||
|
if (selectedDirectory) {
|
||||||
|
const entries = await readDir(selectedDirectory, {
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
|
const splitNumbers = /(\d)+|(\D)+/;
|
||||||
|
this.files.set(
|
||||||
|
entries
|
||||||
|
.sort(this.sortFiles(splitNumbers))
|
||||||
|
.map((entry) => this.mapEntry(entry, splitNumbers))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapEntry(entry: FileEntry, splitNumbers: RegExp): FileOrFolder {
|
||||||
|
return {
|
||||||
|
isDirectory: entry.children != null,
|
||||||
|
name: entry.name || '',
|
||||||
|
children: entry.children
|
||||||
|
?.sort(this.sortFiles(splitNumbers))
|
||||||
|
.map((entry) => this.mapEntry(entry, splitNumbers)),
|
||||||
|
path: entry.path,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private sortFiles = (splitNumbers: RegExp) => (a: FileEntry, b: FileEntry) =>
|
||||||
|
a.name?.localeCompare(b.name ?? '') ?? 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,29 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import {
|
import {
|
||||||
|
AfterViewInit,
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
Component,
|
Component,
|
||||||
|
OnInit,
|
||||||
|
forwardRef,
|
||||||
input,
|
input,
|
||||||
model,
|
model,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { MatButton } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { ListMessage } from '../model/proto-message.model';
|
||||||
import { ProtoFieldComponent } from '../proto-field/proto-field.component';
|
import { ProtoFieldComponent } from '../proto-field/proto-field.component';
|
||||||
import {
|
|
||||||
EnumMessage,
|
|
||||||
ListMessage,
|
|
||||||
ProtoMessageField,
|
|
||||||
} from '../model/proto-message.model';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-list-field',
|
selector: 'app-list-field',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, MatButton, MatIconModule, ProtoFieldComponent],
|
imports: [
|
||||||
template: ` @for(value of values(); track $index) {
|
CommonModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule,
|
||||||
|
forwardRef(() => ProtoFieldComponent),
|
||||||
|
],
|
||||||
|
template: ` <h3>{{ label() }}</h3>
|
||||||
|
@if(values()) { @for(value of values(); track $index) {
|
||||||
<div class="row-wrapper">
|
<div class="row-wrapper">
|
||||||
<app-proto-field
|
<app-proto-field
|
||||||
[configuration]="configuration().subConfiguration"
|
[configuration]="configuration().subConfiguration"
|
||||||
@@ -28,19 +33,24 @@ import {
|
|||||||
<mat-icon>remove</mat-icon>
|
<mat-icon>remove</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
} }
|
||||||
<button mat-icon-button (click)="add()"><mat-icon>add</mat-icon></button>`,
|
<button mat-icon-button (click)="add()"><mat-icon>add</mat-icon></button>`,
|
||||||
styleUrl: './list-field.component.scss',
|
styleUrl: './list-field.component.scss',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class ListFieldComponent {
|
export class ListFieldComponent {
|
||||||
|
label = input<string>();
|
||||||
configuration = input.required<ListMessage>();
|
configuration = input.required<ListMessage>();
|
||||||
values = model<any[]>();
|
values = model<any[]>();
|
||||||
|
|
||||||
add() {
|
add() {
|
||||||
const newValues = this.values();
|
const newValues = this.values();
|
||||||
newValues?.push(null);
|
if (newValues) {
|
||||||
|
newValues.push(null);
|
||||||
this.values.set(newValues);
|
this.values.set(newValues);
|
||||||
|
} else {
|
||||||
|
this.values.set([null]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(index: number) {
|
remove(index: number) {
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ import { ProtoDefinitionService } from '../proto-definition.service';
|
|||||||
imports: [CommonModule, MatButtonModule, MatListModule],
|
imports: [CommonModule, MatButtonModule, MatListModule],
|
||||||
template: `
|
template: `
|
||||||
<h2>Protobuf Definitions</h2>
|
<h2>Protobuf Definitions</h2>
|
||||||
|
<button mat-button (click)="protoSelector.click()">
|
||||||
|
Select definitions
|
||||||
|
</button>
|
||||||
<mat-action-list>
|
<mat-action-list>
|
||||||
@for (item of definitionFiles(); track $index) {
|
@for (item of definitionFiles(); track $index) {
|
||||||
<button mat-list-item (click)="selectProtoDefinition(item)">
|
<button mat-list-item (click)="selectProtoDefinition(item)">
|
||||||
@@ -27,9 +30,6 @@ import { ProtoDefinitionService } from '../proto-definition.service';
|
|||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
</mat-action-list>
|
</mat-action-list>
|
||||||
<button mat-button (click)="protoSelector.click()">
|
|
||||||
Select definitions
|
|
||||||
</button>
|
|
||||||
@if(selectedProtoFile()) {
|
@if(selectedProtoFile()) {
|
||||||
<h3>Messages in {{ selectedProtoFile()?.name }}</h3>
|
<h3>Messages in {{ selectedProtoFile()?.name }}</h3>
|
||||||
<mat-action-list>
|
<mat-action-list>
|
||||||
@@ -67,10 +67,7 @@ export class ProtoDefinitionSelectorComponent {
|
|||||||
return this.isDragging();
|
return this.isDragging();
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(private protoDefinitionService: ProtoDefinitionService) {}
|
||||||
private protoDefinitionService: ProtoDefinitionService,
|
|
||||||
private elementRef: ElementRef
|
|
||||||
) {}
|
|
||||||
|
|
||||||
protected async addDefinitionFiles() {
|
protected async addDefinitionFiles() {
|
||||||
const files = this.protoSelector()?.nativeElement.files;
|
const files = this.protoSelector()?.nativeElement.files;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
MessageConfiguration,
|
MessageConfiguration,
|
||||||
MessageTypeEnum,
|
MessageTypeEnum,
|
||||||
} from '../model/proto-message.model';
|
} from '../model/proto-message.model';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-proto-field',
|
selector: 'app-proto-field',
|
||||||
@@ -28,22 +29,36 @@ import {
|
|||||||
MatCheckboxModule,
|
MatCheckboxModule,
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
MatSelectModule,
|
MatSelectModule,
|
||||||
|
MatInputModule,
|
||||||
],
|
],
|
||||||
template: `@switch (configuration().type) { @case (MessageTypeEnum.String) {
|
template: `@switch (configuration().type) { @case (MessageTypeEnum.String) {
|
||||||
<input [(ngModel)]="value" />
|
<mat-form-field>
|
||||||
|
<mat-label>{{ label() }}</mat-label>
|
||||||
|
<input matInput [(ngModel)]="value" />
|
||||||
|
</mat-form-field>
|
||||||
} @case (MessageTypeEnum.Numeric) {
|
} @case (MessageTypeEnum.Numeric) {
|
||||||
<input type="number" [(ngModel)]="value" />
|
<mat-form-field>
|
||||||
|
<mat-label>{{ label() }}</mat-label>
|
||||||
|
<input matInput type="number" [(ngModel)]="value" />
|
||||||
|
</mat-form-field>
|
||||||
} @case (MessageTypeEnum.Boolean) {
|
} @case (MessageTypeEnum.Boolean) {
|
||||||
<mat-checkbox [(ngModel)]="value"></mat-checkbox>
|
<p>
|
||||||
|
<mat-checkbox [(ngModel)]="value">{{ label() }}</mat-checkbox>
|
||||||
|
</p>
|
||||||
} @case(MessageTypeEnum.Enum) {
|
} @case(MessageTypeEnum.Enum) {
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>{{ label() }}</mat-label>
|
||||||
<mat-select [(value)]="value">
|
<mat-select [(value)]="value">
|
||||||
@for(option of enumConfiguration()!.options; track
|
@for(option of enumConfiguration()!.options; track
|
||||||
enumConfiguration()!.options) {
|
enumConfiguration()!.options) {
|
||||||
<mat-option [value]="option"></mat-option>
|
<mat-option>None</mat-option>
|
||||||
|
<mat-option [value]="option">{{ option }}</mat-option>
|
||||||
}
|
}
|
||||||
</mat-select>
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
} @case (MessageTypeEnum.List) {
|
} @case (MessageTypeEnum.List) {
|
||||||
<app-list-field
|
<app-list-field
|
||||||
|
[label]="label()"
|
||||||
[configuration]="listConfiguration()!"
|
[configuration]="listConfiguration()!"
|
||||||
[(values)]="value"
|
[(values)]="value"
|
||||||
></app-list-field>
|
></app-list-field>
|
||||||
@@ -53,6 +68,7 @@ import {
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class ProtoFieldComponent {
|
export class ProtoFieldComponent {
|
||||||
|
label = input<string>();
|
||||||
configuration = input.required<MessageConfiguration>();
|
configuration = input.required<MessageConfiguration>();
|
||||||
value = model<any>();
|
value = model<any>();
|
||||||
|
|
||||||
|
|||||||
@@ -41,4 +41,6 @@ body {
|
|||||||
--mat-tree-node-min-height: 24px;
|
--mat-tree-node-min-height: 24px;
|
||||||
--mat-tree-node-text-size: 14px;
|
--mat-tree-node-text-size: 14px;
|
||||||
--mdc-icon-button-state-layer-size: 24px;
|
--mdc-icon-button-state-layer-size: 24px;
|
||||||
|
--mat-icon-button-touch-target-display: none;
|
||||||
|
--mdc-list-list-item-one-line-container-height: 24px;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user