App: Einrueckung haelt auch bei Umbruch, Loeschen fragt mittig
Eingerueckt wurde mit fuehrenden Leerzeichen. Die verschieben aber nur die
ERSTE Zeile: Bricht ein langer Name um ("Schublade Links Unten"), faengt die
Fortsetzung wieder ganz links an und die Hierarchie ist weg.
In einem aufklappenden Menue laesst sich das nicht loesen - dort liefert man
nur Text ab, das System ignoriert jedes Layout (auch lineLimit). Die
Lagerort- und Kategorieauswahl erscheint deshalb jetzt als Liste
(.pickerStyle(.navigationLink)). Dort ist jede Zeile eine echte Ansicht, ein
Innenabstand rueckt den ganzen Block ein - auch die zweite Zeile. Das liest
sich bei vielen Lagerorten ohnehin besser als ein langes Aufklappmenue.
Zwei enge Zeilen bleiben Menues: Mindestbestand je Ort bei Artikel und
Gruppe, wo direkt daneben das Mengenfeld steht. Dort wuerde eine
Listenauswahl die Zeile sprengen; sie behalten die Leerzeichen-Einrueckung.
Der Loeschdialog war ein confirmationDialog und fuhr als Blase von unten
herein. Jetzt ein alert: steht mittig und will gelesen werden.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ea7a42ac22
commit
d7002e2252
+25
-3
@@ -1,4 +1,4 @@
|
|||||||
import Foundation
|
import SwiftUI
|
||||||
|
|
||||||
/// Eine Zeile einer flach ausgelegten Baumliste, mit ihrer Tiefe.
|
/// Eine Zeile einer flach ausgelegten Baumliste, mit ihrer Tiefe.
|
||||||
///
|
///
|
||||||
@@ -12,11 +12,33 @@ struct Baumzeile<T: TreeItem>: Identifiable {
|
|||||||
|
|
||||||
var id: T.ID { item.id }
|
var id: T.ID { item.id }
|
||||||
|
|
||||||
/// Name mit Einrueckung je Ebene. Macht die Hierarchie in einer flachen
|
/// Name mit Einrueckung aus fuehrenden Leerzeichen.
|
||||||
/// Auswahl sichtbar, ohne jede Zeile mit dem vollen Pfad zu ueberladen.
|
///
|
||||||
|
/// Nur fuer aufklappende Menues, wo man ausser Text nichts abliefern kann.
|
||||||
|
/// Der Nachteil bleibt dort bestehen: Bricht ein langer Name um, faengt die
|
||||||
|
/// zweite Zeile ganz links an. Wo es geht, `BaumZeile` nehmen.
|
||||||
var label: String { String(repeating: " ", count: tiefe) + item.name }
|
var label: String { String(repeating: " ", count: tiefe) + item.name }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Auswahlzeile mit echter Einrueckung.
|
||||||
|
///
|
||||||
|
/// Fuehrende Leerzeichen verschieben nur die erste Zeile - ein langer Name
|
||||||
|
/// bricht um und die Fortsetzung steht wieder ganz links, womit die Hierarchie
|
||||||
|
/// verloren geht. Ein Innenabstand rueckt dagegen den ganzen Block ein.
|
||||||
|
///
|
||||||
|
/// Verlangt eine Auswahl, die als Liste erscheint (`.pickerStyle(.navigationLink)`):
|
||||||
|
/// In einem aufklappenden Menue zeigt das System nur den Text und ignoriert
|
||||||
|
/// jedes Layout.
|
||||||
|
struct BaumZeile<T: TreeItem>: View {
|
||||||
|
let zeile: Baumzeile<T>
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Text(zeile.item.name)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.padding(.leading, CGFloat(zeile.tiefe) * 16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Bringt eine flache Eltern-Kind-Liste in Baumreihenfolge, mit Tiefe.
|
/// Bringt eine flache Eltern-Kind-Liste in Baumreihenfolge, mit Tiefe.
|
||||||
///
|
///
|
||||||
/// Waisen - deren Elternteil weggefiltert oder geloescht ist - gelten als
|
/// Waisen - deren Elternteil weggefiltert oder geloescht ist - gelten als
|
||||||
|
|||||||
@@ -14,9 +14,12 @@ struct CategoryPicker: View {
|
|||||||
Picker("Kategorie", selection: $selection) {
|
Picker("Kategorie", selection: $selection) {
|
||||||
Text("– keine –").tag(Int?.none)
|
Text("– keine –").tag(Int?.none)
|
||||||
ForEach(baum(gefiltert)) { eintrag in
|
ForEach(baum(gefiltert)) { eintrag in
|
||||||
Text(eintrag.label).tag(Int?.some(eintrag.item.id))
|
BaumZeile(zeile: eintrag).tag(Int?.some(eintrag.item.id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Als Liste statt als Menue: Nur so rueckt ein umbrechender langer
|
||||||
|
// Name auch in der zweiten Zeile ein.
|
||||||
|
.pickerStyle(.navigationLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var gefiltert: [CategoryItem] {
|
private var gefiltert: [CategoryItem] {
|
||||||
|
|||||||
@@ -212,9 +212,10 @@ struct CheckInFormView: View {
|
|||||||
Picker("Lagerort", selection: $locationId) {
|
Picker("Lagerort", selection: $locationId) {
|
||||||
Text("– keiner –").tag(String?.none)
|
Text("– keiner –").tag(String?.none)
|
||||||
ForEach(baum(locations)) { eintrag in
|
ForEach(baum(locations)) { eintrag in
|
||||||
Text(eintrag.label).tag(String?.some(eintrag.item.id))
|
BaumZeile(zeile: eintrag).tag(String?.some(eintrag.item.id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.pickerStyle(.navigationLink)
|
||||||
Button {
|
Button {
|
||||||
locScanError = nil
|
locScanError = nil
|
||||||
locScanPaused = false
|
locScanPaused = false
|
||||||
|
|||||||
@@ -340,8 +340,9 @@ struct ItemEditView: View {
|
|||||||
Section {
|
Section {
|
||||||
Picker("Lagerort", selection: $locationId) {
|
Picker("Lagerort", selection: $locationId) {
|
||||||
Text("– ohne –").tag(String?.none)
|
Text("– ohne –").tag(String?.none)
|
||||||
ForEach(baum(locations)) { e in Text(e.label).tag(String?.some(e.item.id)) }
|
ForEach(baum(locations)) { e in BaumZeile(zeile: e).tag(String?.some(e.item.id)) }
|
||||||
}
|
}
|
||||||
|
.pickerStyle(.navigationLink)
|
||||||
// Lagerort per QR setzen – ohne den (evtl. nicht vorhandenen) QR
|
// Lagerort per QR setzen – ohne den (evtl. nicht vorhandenen) QR
|
||||||
// des Einzelstücks. Praktisch z.B. bei Kleidung.
|
// des Einzelstücks. Praktisch z.B. bei Kleidung.
|
||||||
Button { locScanShown = true } label: {
|
Button { locScanShown = true } label: {
|
||||||
@@ -876,8 +877,9 @@ struct ItemAddSheet: View {
|
|||||||
Stepper("Anzahl: \(count)", value: $count, in: 1...200)
|
Stepper("Anzahl: \(count)", value: $count, in: 1...200)
|
||||||
Picker("Lagerort", selection: $locationId) {
|
Picker("Lagerort", selection: $locationId) {
|
||||||
Text("– ohne –").tag(String?.none)
|
Text("– ohne –").tag(String?.none)
|
||||||
ForEach(baum(locations)) { e in Text(e.label).tag(String?.some(e.item.id)) }
|
ForEach(baum(locations)) { e in BaumZeile(zeile: e).tag(String?.some(e.item.id)) }
|
||||||
}
|
}
|
||||||
|
.pickerStyle(.navigationLink)
|
||||||
Button { locScanShown = true } label: {
|
Button { locScanShown = true } label: {
|
||||||
Label("Lagerort scannen", systemImage: "qrcode.viewfinder")
|
Label("Lagerort scannen", systemImage: "qrcode.viewfinder")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -447,9 +447,10 @@ struct LocationEditor: View {
|
|||||||
Picker("Übergeordnet", selection: $parentId) {
|
Picker("Übergeordnet", selection: $parentId) {
|
||||||
Text("– oberste Ebene –").tag(String?.none)
|
Text("– oberste Ebene –").tag(String?.none)
|
||||||
ForEach(baum(parentOptions)) { eintrag in
|
ForEach(baum(parentOptions)) { eintrag in
|
||||||
Text(eintrag.label).tag(String?.some(eintrag.item.id))
|
BaumZeile(zeile: eintrag).tag(String?.some(eintrag.item.id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.pickerStyle(.navigationLink)
|
||||||
} header: {
|
} header: {
|
||||||
Text("Übergeordneter Lagerort (optional)")
|
Text("Übergeordneter Lagerort (optional)")
|
||||||
}
|
}
|
||||||
@@ -786,9 +787,10 @@ struct CategoryEditor: View {
|
|||||||
Picker("Übergeordnet", selection: $parentId) {
|
Picker("Übergeordnet", selection: $parentId) {
|
||||||
Text("– oberste Ebene –").tag(Int?.none)
|
Text("– oberste Ebene –").tag(Int?.none)
|
||||||
ForEach(baum(all)) { eintrag in
|
ForEach(baum(all)) { eintrag in
|
||||||
Text(eintrag.label).tag(Int?.some(eintrag.item.id))
|
BaumZeile(zeile: eintrag).tag(Int?.some(eintrag.item.id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.pickerStyle(.navigationLink)
|
||||||
} header: {
|
} header: {
|
||||||
Text("Übergeordnete Kategorie (optional)")
|
Text("Übergeordnete Kategorie (optional)")
|
||||||
} footer: {
|
} footer: {
|
||||||
|
|||||||
@@ -152,8 +152,9 @@ private struct LocationPicker: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
Picker(title, selection: $selection) {
|
Picker(title, selection: $selection) {
|
||||||
Text("– ohne Lagerort –").tag(String?.none)
|
Text("– ohne Lagerort –").tag(String?.none)
|
||||||
ForEach(baum(locations)) { e in Text(e.label).tag(String?.some(e.item.id)) }
|
ForEach(baum(locations)) { e in BaumZeile(zeile: e).tag(String?.some(e.item.id)) }
|
||||||
}
|
}
|
||||||
|
.pickerStyle(.navigationLink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -349,8 +349,9 @@ struct ProductDetailView: View {
|
|||||||
Text("Änderungen an Name/Marke/Kategorie sind noch nicht gespeichert. "
|
Text("Änderungen an Name/Marke/Kategorie sind noch nicht gespeichert. "
|
||||||
+ "Foto und Lagerort-Bedarf werden sofort gespeichert.")
|
+ "Foto und Lagerort-Bedarf werden sofort gespeichert.")
|
||||||
}
|
}
|
||||||
.confirmationDialog("„\(current.name)“ löschen?", isPresented: $pendingDelete,
|
// alert und nicht confirmationDialog: Letzteres faehrt als Blase von
|
||||||
titleVisibility: .visible) {
|
// unten herein. Ein Loeschen will mittig stehen und gelesen werden.
|
||||||
|
.alert("„\(current.name)“ löschen?", isPresented: $pendingDelete) {
|
||||||
Button("Löschen", role: .destructive) { Task { await loeschen() } }
|
Button("Löschen", role: .destructive) { Task { await loeschen() } }
|
||||||
Button("Abbrechen", role: .cancel) {}
|
Button("Abbrechen", role: .cancel) {}
|
||||||
} message: {
|
} message: {
|
||||||
@@ -725,8 +726,9 @@ struct LotEditView: View {
|
|||||||
Section("Lagerort") {
|
Section("Lagerort") {
|
||||||
Picker("Lagerort", selection: $locationId) {
|
Picker("Lagerort", selection: $locationId) {
|
||||||
Text("– ohne –").tag(String?.none)
|
Text("– ohne –").tag(String?.none)
|
||||||
ForEach(baum(locations)) { e in Text(e.label).tag(String?.some(e.item.id)) }
|
ForEach(baum(locations)) { e in BaumZeile(zeile: e).tag(String?.some(e.item.id)) }
|
||||||
}
|
}
|
||||||
|
.pickerStyle(.navigationLink)
|
||||||
// Ort per QR am Regal/Fach setzen, statt in der Liste zu suchen.
|
// Ort per QR am Regal/Fach setzen, statt in der Liste zu suchen.
|
||||||
Button { locScanShown = true } label: {
|
Button { locScanShown = true } label: {
|
||||||
Label("Lagerort scannen", systemImage: "qrcode.viewfinder")
|
Label("Lagerort scannen", systemImage: "qrcode.viewfinder")
|
||||||
|
|||||||
Reference in New Issue
Block a user