Polish the New Workout form: segmented When on top, pushed routine list
- When section moves to the top as a segmented control (3 or 4 segments depending on whether Now is offered) - Routine selection is a pushed list with each routine's symbol and color, checkmark on the current pick, select-and-pop - Goal "None" reserves the symbol slot so all goal labels align
This commit is contained in:
@@ -111,6 +111,13 @@ struct ScheduleAddEditView: View {
|
|||||||
/// The live routine currently selected; nil when the stored id no longer resolves.
|
/// The live routine currently selected; nil when the stored id no longer resolves.
|
||||||
private var selectedRoutine: Routine? { routines.first { $0.id == routineID } }
|
private var selectedRoutine: Routine? { routines.first { $0.id == routineID } }
|
||||||
|
|
||||||
|
/// The When choices this presentation offers — "Now" only when adding on today.
|
||||||
|
/// Built as a list (rather than conditionally hiding a tag inside the Picker)
|
||||||
|
/// so the segmented control renders a clean 3- or 4-segment strip.
|
||||||
|
private var availableChoices: [WhenChoice] {
|
||||||
|
offersNow ? WhenChoice.allCases : WhenChoice.allCases.filter { $0 != .now }
|
||||||
|
}
|
||||||
|
|
||||||
private var canSave: Bool {
|
private var canSave: Bool {
|
||||||
guard selectedRoutine != nil else { return false }
|
guard selectedRoutine != nil else { return false }
|
||||||
if when == .fixedDays { return !weekdays.isEmpty }
|
if when == .fixedDays { return !weekdays.isEmpty }
|
||||||
@@ -120,11 +127,11 @@ struct ScheduleAddEditView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
Form {
|
Form {
|
||||||
|
whenSection
|
||||||
routineSection
|
routineSection
|
||||||
if when != .now {
|
if when != .now {
|
||||||
goalSection
|
goalSection
|
||||||
}
|
}
|
||||||
whenSection
|
|
||||||
}
|
}
|
||||||
.navigationTitle(isEditing ? "Edit Workout" : "New Workout")
|
.navigationTitle(isEditing ? "Edit Workout" : "New Workout")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
@@ -148,9 +155,14 @@ struct ScheduleAddEditView: View {
|
|||||||
|
|
||||||
private var routineSection: some View {
|
private var routineSection: some View {
|
||||||
Section {
|
Section {
|
||||||
Picker("Routine", selection: $routineID) {
|
NavigationLink {
|
||||||
ForEach(routines) { routine in
|
RoutinePickerList(routines: routines, selectedID: $routineID)
|
||||||
Text(routine.name).tag(routine.id)
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Text("Routine")
|
||||||
|
Spacer()
|
||||||
|
Text(selectedRoutine?.name ?? "Choose…")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} header: {
|
} header: {
|
||||||
@@ -165,7 +177,14 @@ struct ScheduleAddEditView: View {
|
|||||||
private var goalSection: some View {
|
private var goalSection: some View {
|
||||||
Section {
|
Section {
|
||||||
Picker("Goal", selection: $goal) {
|
Picker("Goal", selection: $goal) {
|
||||||
Text("None").tag(GoalKind?.none)
|
// A hidden symbol placeholder keeps "None" aligned with the
|
||||||
|
// symbol-bearing goal rows below it.
|
||||||
|
Label {
|
||||||
|
Text("None")
|
||||||
|
} icon: {
|
||||||
|
Image(systemName: "tag").hidden()
|
||||||
|
}
|
||||||
|
.tag(GoalKind?.none)
|
||||||
ForEach(GoalKind.orderedCases()) { kind in
|
ForEach(GoalKind.orderedCases()) { kind in
|
||||||
Label(kind.displayName, systemImage: kind.systemImage)
|
Label(kind.displayName, systemImage: kind.systemImage)
|
||||||
.foregroundStyle(Color.color(from: kind.colorName))
|
.foregroundStyle(Color.color(from: kind.colorName))
|
||||||
@@ -183,12 +202,11 @@ struct ScheduleAddEditView: View {
|
|||||||
private var whenSection: some View {
|
private var whenSection: some View {
|
||||||
Section {
|
Section {
|
||||||
Picker("When", selection: $when) {
|
Picker("When", selection: $when) {
|
||||||
ForEach(WhenChoice.allCases) { choice in
|
ForEach(availableChoices) { choice in
|
||||||
if choice != .now || offersNow {
|
Text(choice.displayName).tag(choice)
|
||||||
Text(choice.displayName).tag(choice)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.pickerStyle(.segmented)
|
||||||
|
|
||||||
switch when {
|
switch when {
|
||||||
case .now, .daily:
|
case .now, .daily:
|
||||||
@@ -260,3 +278,42 @@ struct ScheduleAddEditView: View {
|
|||||||
return (existing.map(\.order).max() ?? -1) + 1
|
return (existing.map(\.order).max() ?? -1) + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Routine picker
|
||||||
|
|
||||||
|
/// The pushed routine-selection list: each routine with its own symbol and color
|
||||||
|
/// (the Today board's row treatment), a checkmark on the current selection.
|
||||||
|
/// Tapping selects and pops back.
|
||||||
|
private struct RoutinePickerList: View {
|
||||||
|
let routines: [Routine]
|
||||||
|
@Binding var selectedID: String
|
||||||
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
List(routines) { routine in
|
||||||
|
Button {
|
||||||
|
selectedID = routine.id
|
||||||
|
dismiss()
|
||||||
|
} label: {
|
||||||
|
HStack(spacing: 12) {
|
||||||
|
Image(systemName: routine.systemImage)
|
||||||
|
.font(.title3)
|
||||||
|
.foregroundStyle(Color.color(from: routine.color))
|
||||||
|
.frame(width: 32)
|
||||||
|
Text(routine.name)
|
||||||
|
.foregroundStyle(.primary)
|
||||||
|
Spacer()
|
||||||
|
if routine.id == selectedID {
|
||||||
|
Image(systemName: "checkmark")
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundStyle(Color.accentColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
}
|
||||||
|
.navigationTitle("Routine")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user