Use accordions for nested field types
All checks were successful
release-nightly / macos (push) Successful in 2m9s
release-nightly / web-demo (push) Successful in 4m53s

This commit is contained in:
2025-05-14 20:38:14 +09:30
parent cdeccf33b3
commit 8003a8fee9
6 changed files with 90 additions and 60 deletions

View File

@@ -1,6 +1,7 @@
:host { :host {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin: 0.5rem 0;
} }
.row-wrapper { .row-wrapper {
@@ -11,7 +12,3 @@
app-proto-field { app-proto-field {
flex: 1; flex: 1;
} }
.add-button {
align-self: flex-end;
}

View File

@@ -11,30 +11,40 @@ 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 { ListMessage } from '../../model/proto-message.model';
import { ProtoFieldComponent } from '../proto-field/proto-field.component'; import { ProtoFieldComponent } from '../proto-field/proto-field.component';
import { MatExpansionModule } from '@angular/material/expansion';
@Component({ @Component({
selector: 'app-list-field', selector: 'app-list-field',
imports: [ imports: [
MatButtonModule, MatButtonModule,
MatExpansionModule,
MatIconModule, MatIconModule,
forwardRef(() => ProtoFieldComponent), forwardRef(() => ProtoFieldComponent),
], ],
template: `<h3>{{ label() }}</h3> template: `
@if(values()) { @for(value of values(); track $index) { <mat-accordion>
<div class="row-wrapper"> <mat-expansion-panel [expanded]="true">
<app-proto-field <mat-expansion-panel-header>
[configuration]="configuration().subConfiguration" <mat-panel-title>{{ label() }}</mat-panel-title>
[value]="value" </mat-expansion-panel-header>
(valueChange)="updateValue($index, $event)" @if(values()) { @for(value of values(); track $index) {
></app-proto-field> <div class="row-wrapper">
<button mat-icon-button (click)="remove($index)"> <app-proto-field
<mat-icon>remove</mat-icon> [configuration]="configuration().subConfiguration"
</button> [value]="value"
</div> (valueChange)="updateValue($index, $event)"
} } ></app-proto-field>
<button mat-icon-button class="add-button" (click)="add()"> <button mat-icon-button (click)="remove($index)">
<mat-icon>add</mat-icon> <mat-icon>remove</mat-icon>
</button>`, </button>
</div>
} }
<button mat-icon-button class="add-button" (click)="add()">
<mat-icon>add</mat-icon>
</button>
</mat-expansion-panel>
</mat-accordion>
`,
styleUrl: './list-field.component.scss', styleUrl: './list-field.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })

View File

@@ -1,6 +1,7 @@
:host { :host {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin: 0.5rem 0;
} }
.row-wrapper { .row-wrapper {
@@ -12,10 +13,6 @@ app-proto-field {
flex: 1; flex: 1;
&:first-child { &:first-child {
margin-right: 10px; margin-right: 0.5rem;
} }
} }
.add-button {
align-self: flex-end;
}

View File

@@ -11,6 +11,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MapMessage } from '../../model/proto-message.model'; import { MapMessage } from '../../model/proto-message.model';
import { ProtoFieldComponent } from '../proto-field/proto-field.component'; import { ProtoFieldComponent } from '../proto-field/proto-field.component';
import { MatExpansionModule } from '@angular/material/expansion';
const keyIsEmpty = (key: string | number) => key == null || key === ''; const keyIsEmpty = (key: string | number) => key == null || key === '';
@@ -18,30 +19,39 @@ const keyIsEmpty = (key: string | number) => key == null || key === '';
selector: 'app-map-field', selector: 'app-map-field',
imports: [ imports: [
forwardRef(() => ProtoFieldComponent), forwardRef(() => ProtoFieldComponent),
MatExpansionModule,
MatIconModule, MatIconModule,
MatButtonModule, MatButtonModule,
], ],
template: `<h3>{{ label() }}</h3> template: `
@if(valuePairs()) { @for(value of valuePairs(); track $index) { <mat-accordion>
<div class="row-wrapper"> <mat-expansion-panel [expanded]="true">
<app-proto-field <mat-expansion-panel-header>
[configuration]="configuration().keyConfiguration" <mat-panel-title>{{ label() }}</mat-panel-title>
[value]="value[0]" </mat-expansion-panel-header>
(valueChange)="updateKey($index, $event)" @if(valuePairs()) { @for(value of valuePairs(); track $index) {
></app-proto-field> <div class="row-wrapper">
<app-proto-field <app-proto-field
[configuration]="configuration().valueConfiguration" [configuration]="configuration().keyConfiguration"
[value]="value[1]" [value]="value[0]"
(valueChange)="updateValue($index, $event)" (valueChange)="updateKey($index, $event)"
></app-proto-field> ></app-proto-field>
<button mat-icon-button (click)="remove($index)"> <app-proto-field
<mat-icon>remove</mat-icon> [configuration]="configuration().valueConfiguration"
</button> [value]="value[1]"
</div> (valueChange)="updateValue($index, $event)"
} } ></app-proto-field>
<button mat-icon-button class="add-button" (click)="add()"> <button mat-icon-button (click)="remove($index)">
<mat-icon>add</mat-icon> <mat-icon>remove</mat-icon>
</button>`, </button>
</div>
} }
<button mat-icon-button class="add-button" (click)="add()">
<mat-icon>add</mat-icon>
</button>
</mat-expansion-panel>
</mat-accordion>
`,
styleUrl: './map-field.component.scss', styleUrl: './map-field.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })

View File

@@ -0,0 +1,4 @@
:host {
display: block;
margin: 0.5rem 0;
}

View File

@@ -8,23 +8,35 @@ import {
} from '@angular/core'; } from '@angular/core';
import { ObjectMessage } from '../../model/proto-message.model'; import { ObjectMessage } from '../../model/proto-message.model';
import { ProtoFieldComponent } from '../proto-field/proto-field.component'; import { ProtoFieldComponent } from '../proto-field/proto-field.component';
import { MatExpansionModule } from '@angular/material/expansion';
@Component({ @Component({
selector: 'app-object-field', selector: 'app-object-field',
imports: [forwardRef(() => ProtoFieldComponent)], imports: [MatExpansionModule, forwardRef(() => ProtoFieldComponent)],
template: `<h3> template: `
{{ label() }} ({{ configuration().messageDefinition.name }}) <mat-accordion>
</h3> <mat-expansion-panel [expanded]="true">
<div> <mat-expansion-panel-header>
@for (item of configuration().messageDefinition.values; track $index) { <mat-panel-title>
<app-proto-field {{ label() ?? configuration().messageDefinition.name }}
[label]="item.friendlyName || item.name" </mat-panel-title>
[configuration]="item.configuration" @if(label()) {
[value]="currentValue()[item.name]" <mat-panel-description>
(valueChange)="updateValue(item.name, $event)" {{ configuration().messageDefinition.name }}
></app-proto-field> </mat-panel-description>
} }
</div>`, </mat-expansion-panel-header>
@for (item of configuration().messageDefinition.values; track $index) {
<app-proto-field
[label]="item.friendlyName || item.name"
[configuration]="item.configuration"
[value]="currentValue()[item.name]"
(valueChange)="updateValue(item.name, $event)"
></app-proto-field>
}
</mat-expansion-panel>
</mat-accordion>
`,
styleUrl: './object-field.component.scss', styleUrl: './object-field.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })