diff --git a/src/app/editor/editor.component.ts b/src/app/editor/editor.component.ts
index 51dcfb1..98b425a 100644
--- a/src/app/editor/editor.component.ts
+++ b/src/app/editor/editor.component.ts
@@ -11,7 +11,7 @@ import {
} from '@angular/core';
import hljs from 'highlight.js/lib/core';
import { ProtoMessage } from '../model/proto-message.model';
-import { ProtoFieldComponent } from '../proto-field/proto-field.component';
+import { ProtoFieldComponent } from './proto-field/proto-field.component';
import { DomSanitizer } from '@angular/platform-browser';
@Component({
diff --git a/src/app/list-field/list-field.component.scss b/src/app/editor/list-field/list-field.component.scss
similarity index 100%
rename from src/app/list-field/list-field.component.scss
rename to src/app/editor/list-field/list-field.component.scss
diff --git a/src/app/list-field/list-field.component.ts b/src/app/editor/list-field/list-field.component.ts
similarity index 91%
rename from src/app/list-field/list-field.component.ts
rename to src/app/editor/list-field/list-field.component.ts
index 0253f90..1115a8b 100644
--- a/src/app/list-field/list-field.component.ts
+++ b/src/app/editor/list-field/list-field.component.ts
@@ -10,7 +10,7 @@ import {
} from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
-import { ListMessage } from '../model/proto-message.model';
+import { ListMessage } from '../../model/proto-message.model';
import { ProtoFieldComponent } from '../proto-field/proto-field.component';
@Component({
@@ -22,7 +22,7 @@ import { ProtoFieldComponent } from '../proto-field/proto-field.component';
MatIconModule,
forwardRef(() => ProtoFieldComponent),
],
- template: `
{{ label() }}
+ template: `{{ label() }}
@if(values()) { @for(value of values(); track $index) {
key == null || key === '';
+
+@Component({
+ selector: 'app-map-field',
+ standalone: true,
+ imports: [
+ CommonModule,
+ forwardRef(() => ProtoFieldComponent),
+ MatIconModule,
+ MatButtonModule,
+ ],
+ template: `{{ label() }}
+ @if(valuePairs()) { @for(value of valuePairs(); track $index) {
+
+ } }
+ `,
+ styleUrl: './map-field.component.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class MapFieldComponent {
+ label = input();
+ configuration = input.required();
+ values = model>();
+
+ valuePairs = signal([]);
+
+ private changedInternal = false;
+
+ constructor() {
+ effect(
+ () => {
+ // TODO: Super hacky but can't really think of another way to keep these in sync
+ // without removing an entry when the key gets blanked. Would need an alternate
+ // design that updated on blur only perhaps
+ if (this.changedInternal) {
+ this.changedInternal = false;
+ return;
+ }
+ const values = this.values();
+ if (values) {
+ this.valuePairs.set(Object.entries(values));
+ }
+ },
+ { allowSignalWrites: true }
+ );
+ }
+
+ add() {
+ const existingValues = this.valuePairs();
+ if (existingValues) {
+ const newValues = [...existingValues];
+ newValues.push([undefined, undefined]);
+ this.valuePairs.set(newValues);
+ } else {
+ this.valuePairs.set([[undefined, undefined]]);
+ }
+ }
+
+ remove(index: number) {
+ const newValues = { ...this.values()! };
+ const key = this.valuePairs()[index][0];
+ delete newValues[key];
+ this.changedInternal = true;
+ this.values.set(newValues);
+
+ const newValuePairs = [...this.valuePairs()];
+ newValuePairs.splice(index, 1);
+ this.valuePairs.set(newValuePairs);
+ }
+
+ updateKey(index: number, key: string | number) {
+ const values = { ...this.values() };
+ const valuePairs = [...this.valuePairs()];
+ const pair = [...valuePairs[index]];
+ if (!keyIsEmpty(pair[0])) {
+ delete values[pair[0]];
+ }
+ if (!keyIsEmpty(key)) {
+ values[key] = pair[1];
+ }
+ this.changedInternal = true;
+ this.values.set(values);
+
+ pair[0] = key;
+ valuePairs[index] = pair;
+ this.valuePairs.set(valuePairs);
+ }
+
+ updateValue(index: number, value: any) {
+ const valuePairs = [...this.valuePairs()];
+ const pair = [...valuePairs[index]];
+ if (!keyIsEmpty(pair[0])) {
+ let existing = { ...this.values()! };
+ existing[pair[0]] = value;
+ this.changedInternal = true;
+ this.values.set(existing);
+ }
+ pair[1] = value;
+ valuePairs[index] = pair;
+ this.valuePairs.set(valuePairs);
+ }
+}
diff --git a/src/app/proto-field/proto-field.component.scss b/src/app/editor/proto-field/proto-field.component.scss
similarity index 100%
rename from src/app/proto-field/proto-field.component.scss
rename to src/app/editor/proto-field/proto-field.component.scss
diff --git a/src/app/proto-field/proto-field.component.ts b/src/app/editor/proto-field/proto-field.component.ts
similarity index 73%
rename from src/app/proto-field/proto-field.component.ts
rename to src/app/editor/proto-field/proto-field.component.ts
index b294cf0..53ccf08 100644
--- a/src/app/proto-field/proto-field.component.ts
+++ b/src/app/editor/proto-field/proto-field.component.ts
@@ -9,15 +9,17 @@ import {
import { FormsModule } from '@angular/forms';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
-import { ListFieldComponent } from '../list-field/list-field.component';
import {
EnumMessage,
ListMessage,
+ MapMessage,
MessageConfiguration,
MessageTypeEnum,
-} from '../model/proto-message.model';
-import { MatInputModule } from '@angular/material/input';
+} from '../../model/proto-message.model';
+import { ListFieldComponent } from '../list-field/list-field.component';
+import { MapFieldComponent } from '../map-field/map-field.component';
@Component({
selector: 'app-proto-field',
@@ -26,6 +28,7 @@ import { MatInputModule } from '@angular/material/input';
CommonModule,
FormsModule,
ListFieldComponent,
+ MapFieldComponent,
MatCheckboxModule,
MatFormFieldModule,
MatSelectModule,
@@ -49,7 +52,7 @@ import { MatInputModule } from '@angular/material/input';
{{ label() }}
- @for(option of enumConfiguration()!.options; track
+ @for(option of enumConfiguration().options; track
enumConfiguration()!.options) {
None
{{ option }}
@@ -59,11 +62,16 @@ import { MatInputModule } from '@angular/material/input';
} @case (MessageTypeEnum.List) {
- } @case (MessageTypeEnum.Map){} @case (MessageTypeEnum.Object) {} @case
- (MessageTypeEnum.Raw) {}}`,
+ } @case (MessageTypeEnum.Map){
+
+ } @case (MessageTypeEnum.Object) {} @case (MessageTypeEnum.Raw) {}}`,
styleUrl: './proto-field.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
@@ -72,19 +80,17 @@ export class ProtoFieldComponent {
configuration = input.required();
value = model();
- protected enumConfiguration = computed(() => {
- if (this.configuration().type === MessageTypeEnum.Enum) {
- return this.configuration() as EnumMessage;
- }
- return;
- });
+ protected enumConfiguration = computed(
+ () => this.configuration() as EnumMessage
+ );
- protected listConfiguration = computed(() => {
- if (this.configuration().type === MessageTypeEnum.List) {
- return this.configuration() as ListMessage;
- }
- return;
- });
+ protected listConfiguration = computed(
+ () => this.configuration() as ListMessage
+ );
+
+ protected mapConfiguration = computed(
+ () => this.configuration() as MapMessage
+ );
protected readonly MessageTypeEnum = MessageTypeEnum;
}
diff --git a/src/app/list-field/list-field.component.spec.ts b/src/app/list-field/list-field.component.spec.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/src/app/proto-definition-selector/proto-definition-selector.component.ts b/src/app/proto-definition-selector/proto-definition-selector.component.ts
index 6d9c439..c66c169 100644
--- a/src/app/proto-definition-selector/proto-definition-selector.component.ts
+++ b/src/app/proto-definition-selector/proto-definition-selector.component.ts
@@ -12,7 +12,7 @@ import {
import { MatButtonModule } from '@angular/material/button';
import { MatListModule } from '@angular/material/list';
import { ProtoMessage } from '../model/proto-message.model';
-import { ProtoDefinitionService } from '../proto-definition.service';
+import { ProtoDefinitionService } from './proto-definition.service';
@Component({
selector: 'app-proto-definition-selector',
diff --git a/src/app/proto-definition.service.spec.ts b/src/app/proto-definition-selector/proto-definition.service.spec.ts
similarity index 98%
rename from src/app/proto-definition.service.spec.ts
rename to src/app/proto-definition-selector/proto-definition.service.spec.ts
index 417cd48..1f0d305 100644
--- a/src/app/proto-definition.service.spec.ts
+++ b/src/app/proto-definition-selector/proto-definition.service.spec.ts
@@ -8,7 +8,7 @@ import {
MessageTypeEnum,
ObjectMessage,
ProtoMessage,
-} from './model/proto-message.model';
+} from '../model/proto-message.model';
import { ProtoDefinitionService } from './proto-definition.service';
let testProto = `
diff --git a/src/app/proto-definition.service.ts b/src/app/proto-definition-selector/proto-definition.service.ts
similarity index 99%
rename from src/app/proto-definition.service.ts
rename to src/app/proto-definition-selector/proto-definition.service.ts
index ed61208..b9f7a57 100644
--- a/src/app/proto-definition.service.ts
+++ b/src/app/proto-definition-selector/proto-definition.service.ts
@@ -14,7 +14,7 @@ import {
ProtoMessage,
StringMessage,
UnknownProto,
-} from './model/proto-message.model';
+} from '../model/proto-message.model';
@Injectable({
providedIn: 'root',