This commit is contained in:
Andras Schmelczer 2026-05-30 14:33:39 +01:00
parent e2a60e71a3
commit 003f38ea60
36 changed files with 1543 additions and 1287 deletions

View file

@ -5,7 +5,9 @@ import {
output,
OnInit,
inject,
DestroyRef,
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { Tower, HslColor } from '../../models';
import { ColorPickerComponent } from '../shared/color-picker/color-picker.component';
@ -39,12 +41,12 @@ export interface TowerSettingsResult {
<lt-color-picker [color]="currentColor" (colorChange)="onColorChange($event)" />
</div>
<button type="submit" [disabled]="form.invalid">
{{ tower() ? 'Save' : 'Create tower' }}
</button>
@if (tower()) {
<!-- Editing an existing tower: changes auto-save, so there's no Save
button only the destructive action remains explicit. -->
<button type="button" (click)="delete.emit()">Delete tower</button>
} @else {
<button type="submit" [disabled]="form.invalid">Create tower</button>
}
</form>
`,
@ -86,14 +88,14 @@ export interface TowerSettingsResult {
border: 0;
}
// Generous gap between the name input and the color picker — the picker
// is a substantial control and crowding it against the title looks busy.
.picker-row {
padding-top: var(--medium-padding);
}
button {
display: block;
// Stay full-width on mobile, but switch to flex so forms.scss's
// bottom-alignment keeps the underline hugging the label in the 42px
// tap target (plain block centres the text and strands the underline).
@media (max-width: $mobile-width) {
display: flex;
}
}
}
`,
@ -105,6 +107,7 @@ export class TowerSettingsComponent implements OnInit {
readonly close = output<void>();
private readonly fb = inject(FormBuilder);
private readonly destroyRef = inject(DestroyRef);
form = this.fb.group({
name: ['', [Validators.required, Validators.maxLength(200)]],
@ -117,17 +120,36 @@ export class TowerSettingsComponent implements OnInit {
if (t) {
this.form.patchValue({ name: t.name });
this.currentColor = { ...t.base_color };
// Edit mode: persist name changes as they happen. Wire this up *after*
// the initial patchValue so seeding the form doesn't fire a save.
this.form.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => this.autoSave());
}
}
onColorChange(color: HslColor): void {
this.currentColor = color;
// In edit mode the picker is a live control — commit each change.
if (this.tower()) this.autoSave();
}
onSubmit(): void {
// Only the create flow reaches here via its Submit button; edit mode
// auto-saves (and Enter on the single field is a harmless redundant save).
if (this.form.invalid) return;
const v = this.form.value;
this.save.emit({ name: v.name ?? '', base_color: this.currentColor });
this.emitSave();
}
/** Emit a save only when the form is valid (skips e.g. an empty name). */
private autoSave(): void {
if (this.form.invalid) return;
this.emitSave();
}
private emitSave(): void {
this.save.emit({ name: this.form.value.name ?? '', base_color: this.currentColor });
}
}