refactor: extract reusable SportSelectorGrid component
Create unified sport selector grid used across Home (Quick Start), Trip Creation, and Progress views. Removes duplicate button implementations and ensures consistent grid layout with centered bottom row. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -169,51 +169,15 @@ struct HomeView: View {
|
||||
// MARK: - Quick Actions
|
||||
|
||||
private var quickActions: some View {
|
||||
let sports = Sport.supported
|
||||
let rows = sports.chunked(into: 4)
|
||||
|
||||
return VStack(alignment: .leading, spacing: Theme.Spacing.sm) {
|
||||
VStack(alignment: .leading, spacing: Theme.Spacing.sm) {
|
||||
Text("Quick Start")
|
||||
.font(.title2)
|
||||
.foregroundStyle(Theme.textPrimary(colorScheme))
|
||||
|
||||
GeometryReader { geometry in
|
||||
let spacing = Theme.Spacing.sm
|
||||
let buttonWidth = (geometry.size.width - 3 * spacing) / 4
|
||||
|
||||
VStack(spacing: Theme.Spacing.md) {
|
||||
ForEach(Array(rows.enumerated()), id: \.offset) { _, row in
|
||||
if row.count == 4 {
|
||||
// Full row - evenly distributed
|
||||
HStack(spacing: spacing) {
|
||||
ForEach(row) { sport in
|
||||
QuickSportButton(sport: sport) {
|
||||
selectedSport = sport
|
||||
showNewTrip = true
|
||||
}
|
||||
.frame(width: buttonWidth)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Partial row - centered with same button width and spacing
|
||||
HStack {
|
||||
Spacer()
|
||||
HStack(spacing: spacing) {
|
||||
ForEach(row) { sport in
|
||||
QuickSportButton(sport: sport) {
|
||||
selectedSport = sport
|
||||
showNewTrip = true
|
||||
}
|
||||
.frame(width: buttonWidth)
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SportSelectorGrid { sport in
|
||||
selectedSport = sport
|
||||
showNewTrip = true
|
||||
}
|
||||
.frame(height: 180) // Approximate height for 2 rows
|
||||
.padding(.horizontal, Theme.Spacing.md)
|
||||
.padding(.vertical, Theme.Spacing.md)
|
||||
.background(Theme.cardBackground(colorScheme))
|
||||
@@ -371,45 +335,6 @@ struct HomeView: View {
|
||||
|
||||
// MARK: - Supporting Views
|
||||
|
||||
struct QuickSportButton: View {
|
||||
let sport: Sport
|
||||
let action: () -> Void
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
@State private var isPressed = false
|
||||
|
||||
var body: some View {
|
||||
Button(action: action) {
|
||||
VStack(spacing: 6) {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(sport.themeColor.opacity(0.15))
|
||||
.frame(width: 48, height: 48)
|
||||
|
||||
Image(systemName: sport.iconName)
|
||||
.font(.title3)
|
||||
.foregroundStyle(sport.themeColor)
|
||||
}
|
||||
|
||||
Text(sport.rawValue)
|
||||
.font(.caption2)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.scaleEffect(isPressed ? 0.9 : 1.0)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.simultaneousGesture(
|
||||
DragGesture(minimumDistance: 0)
|
||||
.onChanged { _ in
|
||||
withAnimation(Theme.Animation.spring) { isPressed = true }
|
||||
}
|
||||
.onEnded { _ in
|
||||
withAnimation(Theme.Animation.spring) { isPressed = false }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct SavedTripCard: View {
|
||||
let savedTrip: SavedTrip
|
||||
let trip: Trip
|
||||
|
||||
Reference in New Issue
Block a user