edit hinter button

This commit is contained in:
Bastian Wagner
2026-06-09 17:00:47 +02:00
parent 16605c961f
commit db525564c8
3 changed files with 104 additions and 40 deletions

View File

@@ -36,32 +36,44 @@
<mat-card class="editor-card" appearance="outlined"> <mat-card class="editor-card" appearance="outlined">
<mat-card-header> <mat-card-header>
<mat-card-title>Details</mat-card-title> <mat-card-title>Details</mat-card-title>
@if (!isCreateMode()) {
<button mat-stroked-button type="button" (click)="showEditor() ? cancelEditing() : startEditing()">
<mat-icon aria-hidden="true">{{ showEditor() ? 'close' : 'edit' }}</mat-icon>
{{ showEditor() ? 'Abbrechen' : 'Bearbeiten' }}
</button>
}
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<form [formGroup]="listForm" class="list-form" (ngSubmit)="saveList()"> @if (showEditor()) {
<mat-form-field appearance="outline"> <form [formGroup]="listForm" class="list-form" (ngSubmit)="saveList()">
<mat-label>Titel</mat-label> <mat-form-field appearance="outline">
<input matInput formControlName="name" autocomplete="off" /> <mat-label>Titel</mat-label>
@if (listForm.controls.name.hasError('required')) { <input matInput formControlName="name" autocomplete="off" />
<mat-error>Titel ist erforderlich.</mat-error> @if (listForm.controls.name.hasError('required')) {
} <mat-error>Titel ist erforderlich.</mat-error>
</mat-form-field> }
</mat-form-field>
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-label>Beschreibung</mat-label> <mat-label>Beschreibung</mat-label>
<textarea matInput formControlName="description" rows="4"></textarea> <textarea matInput formControlName="description" rows="4"></textarea>
</mat-form-field> </mat-form-field>
<button mat-flat-button type="submit" [disabled]="saving()"> <button mat-flat-button type="submit" [disabled]="saving()">
@if (saving()) { @if (saving()) {
<mat-progress-spinner mode="indeterminate" diameter="18" /> <mat-progress-spinner mode="indeterminate" diameter="18" />
} @else { } @else {
<mat-icon aria-hidden="true">save</mat-icon> <mat-icon aria-hidden="true">save</mat-icon>
} }
{{ isCreateMode() ? 'Liste anlegen' : 'Speichern' }} {{ isCreateMode() ? 'Liste anlegen' : 'Speichern' }}
</button> </button>
</form> </form>
} @else {
<div class="list-summary">
<p>{{ list()?.description || 'Keine Beschreibung hinterlegt.' }}</p>
</div>
}
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
@@ -78,26 +90,28 @@
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<form [formGroup]="itemForm" class="item-form" (ngSubmit)="addItem()"> @if (showEditor()) {
<mat-form-field appearance="outline"> <form [formGroup]="itemForm" class="item-form" (ngSubmit)="addItem()">
<mat-label>Neues Item</mat-label> <mat-form-field appearance="outline">
<input matInput formControlName="title" autocomplete="off" [disabled]="!canEditItems()" /> <mat-label>Neues Item</mat-label>
@if (itemForm.controls.title.hasError('required')) { <input matInput formControlName="title" autocomplete="off" [disabled]="!canEditItems()" />
<mat-error>Item-Titel ist erforderlich.</mat-error> @if (itemForm.controls.title.hasError('required')) {
} <mat-error>Item-Titel ist erforderlich.</mat-error>
</mat-form-field> }
</mat-form-field>
<mat-checkbox formControlName="required">Pflicht</mat-checkbox> <mat-checkbox formControlName="required">Pflicht</mat-checkbox>
<button mat-flat-button type="submit" [disabled]="addingItem() || !canEditItems()"> <button mat-flat-button type="submit" [disabled]="addingItem() || !canEditItems()">
@if (addingItem()) { @if (addingItem()) {
<mat-progress-spinner mode="indeterminate" diameter="18" /> <mat-progress-spinner mode="indeterminate" diameter="18" />
} @else { } @else {
<mat-icon aria-hidden="true">add</mat-icon> <mat-icon aria-hidden="true">add</mat-icon>
} }
Hinzufügen Hinzufügen
</button> </button>
</form> </form>
}
@if (!canEditItems()) { @if (!canEditItems()) {
<div class="inline-empty"> <div class="inline-empty">
@@ -143,7 +157,7 @@
<a mat-button routerLink="/lists" class="secondary-back"> <a mat-button routerLink="/lists" class="secondary-back">
<mat-icon aria-hidden="true">arrow_back</mat-icon> <mat-icon aria-hidden="true">arrow_back</mat-icon>
Zur Listenuebersicht Zur Listenübersicht
</a> </a>
} }
</section> </section>

View File

@@ -34,6 +34,16 @@
background: color-mix(in srgb, var(--mat-sys-surface-container-low) 94%, white); background: color-mix(in srgb, var(--mat-sys-surface-container-low) 94%, white);
} }
.editor-card mat-card-header {
align-items: center;
justify-content: space-between;
gap: 0.75rem;
}
.editor-card mat-card-header button {
flex: 0 0 auto;
}
.list-form, .list-form,
.item-form { .item-form {
display: grid; display: grid;
@@ -72,6 +82,17 @@
font-size: 48px; font-size: 48px;
} }
.list-summary {
padding-top: 0.75rem;
}
.list-summary p {
margin: 0;
color: var(--mat-sys-on-surface-variant);
overflow-wrap: anywhere;
line-height: 1.45;
}
.error-state mat-icon { .error-state mat-icon {
color: var(--mat-sys-error); color: var(--mat-sys-error);
} }

View File

@@ -46,10 +46,12 @@ export class ListDetailComponent implements OnInit {
protected readonly isCreateMode = signal(false); protected readonly isCreateMode = signal(false);
protected readonly loading = signal(true); protected readonly loading = signal(true);
protected readonly saving = signal(false); protected readonly saving = signal(false);
protected readonly editing = signal(false);
protected readonly addingItem = signal(false); protected readonly addingItem = signal(false);
protected readonly errorMessage = signal<string | null>(null); protected readonly errorMessage = signal<string | null>(null);
protected readonly updatingItemId = signal<string | null>(null); protected readonly updatingItemId = signal<string | null>(null);
protected readonly canEditItems = computed(() => Boolean(this.list()?.id)); protected readonly canEditItems = computed(() => Boolean(this.list()?.id));
protected readonly showEditor = computed(() => this.isCreateMode() || this.editing());
protected readonly listForm = this.formBuilder.group({ protected readonly listForm = this.formBuilder.group({
name: ['', [Validators.required]], name: ['', [Validators.required]],
@@ -66,6 +68,7 @@ export class ListDetailComponent implements OnInit {
if (this.isCreateMode()) { if (this.isCreateMode()) {
this.loading.set(false); this.loading.set(false);
this.editing.set(true);
this.listForm.reset({ name: '', description: '' }); this.listForm.reset({ name: '', description: '' });
return; return;
} }
@@ -128,7 +131,10 @@ export class ListDetailComponent implements OnInit {
this.setList(list); this.setList(list);
if (this.isCreateMode()) { if (this.isCreateMode()) {
this.isCreateMode.set(false); this.isCreateMode.set(false);
this.editing.set(false);
void this.router.navigate(['/lists', list.id], { replaceUrl: true }); void this.router.navigate(['/lists', list.id], { replaceUrl: true });
} else {
this.editing.set(false);
} }
this.snackBar.open('Liste gespeichert.', 'OK', { duration: 2500 }); this.snackBar.open('Liste gespeichert.', 'OK', { duration: 2500 });
}, },
@@ -196,6 +202,29 @@ export class ListDetailComponent implements OnInit {
}); });
} }
protected startEditing(): void {
this.editing.set(true);
}
protected cancelEditing(): void {
const list = this.list();
if (this.isCreateMode()) {
void this.router.navigateByUrl('/lists');
return;
}
if (list) {
this.listForm.reset({
name: list.name,
description: list.description ?? '',
});
}
this.itemForm.reset({ title: '', required: true });
this.editing.set(false);
}
protected checkedCount(list: UserList): number { protected checkedCount(list: UserList): number {
return list.items.filter((item) => item.checked).length; return list.items.filter((item) => item.checked).length;
} }