Files
Cable/Cable/Systems/SystemsOnboardingView.swift
Stefan Lange-Hegermann 6258a6a66f more consitancy
2025-10-22 22:43:03 +02:00

130 lines
4.7 KiB
Swift

import SwiftUI
struct SystemsOnboardingView: View {
@State private var systemName: String = String(localized: "default.system.name", comment: "Default placeholder name for a system")
@State private var carouselStep = 0
@FocusState private var isFieldFocused: Bool
let onCreate: (String) -> Void
private let imageNames = [
"van-onboarding",
"cabin-onboarding",
"boat-onboarding"
]
private let timer = Timer.publish(every: 8, on: .main, in: .common).autoconnect()
private let animationDuration = 0.8
private var loopingImages: [String] {
guard let first = imageNames.first else { return [] }
return imageNames + [first]
}
var body: some View {
VStack() {
Spacer(minLength: 32)
OnboardingCarouselView(images: loopingImages, step: carouselStep)
.frame(minHeight: 80, maxHeight: 240)
.padding(.horizontal, 0)
VStack(spacing: 12) {
Text("Create your first system")
.font(.title2.weight(.semibold))
.multilineTextAlignment(.center)
Text("Give your setup a name so **Cable by VoltPlan** can organize loads, wiring, and recommendations in one place.")
.font(.body)
.foregroundStyle(Color.secondary)
.multilineTextAlignment(.center)
.frame(minHeight: 96)
.padding(.horizontal, 12)
}
.padding(.horizontal, 24)
Spacer()
VStack(spacing: 16) {
HStack(spacing: 12) {
Image(systemName: "sparkles")
.font(.system(size: 18, weight: .semibold))
.foregroundStyle(isFieldFocused ? Color.accentColor : Color.accentColor.opacity(0.6))
TextField("System Name", text: $systemName)
.textInputAutocapitalization(.words)
.disableAutocorrection(true)
.focused($isFieldFocused)
.submitLabel(.done)
.onSubmit(createSystem)
}
.padding(.horizontal, 16)
.padding(.vertical, 14)
.background(
RoundedRectangle(cornerRadius: 18, style: .continuous)
.fill(Color(.secondarySystemBackground))
)
.overlay(
RoundedRectangle(cornerRadius: 18, style: .continuous)
.stroke(isFieldFocused ? Color.accentColor : Color.clear, lineWidth: 1)
)
.shadow(color: Color.black.opacity(0.08), radius: 12, x: 0, y: 6)
.animation(.easeInOut(duration: 0.2), value: isFieldFocused)
Button(action: createSystem) {
HStack(spacing:8) {
Image(systemName: "plus.circle.fill")
.font(.system(size: 16))
Text("Create System")
.font(.headline.weight(.semibold))
}
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.frame(height: 48)
.background(Color.blue)
.cornerRadius(12)
}
.accessibilityIdentifier("create-system-button")
.buttonStyle(.plain)
}
.padding(.horizontal, 24)
}
.padding(.bottom, 32)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(.systemGroupedBackground))
.onAppear(perform: resetState)
.onReceive(timer) { _ in advanceCarousel() }
}
private func resetState() {
systemName = String(localized: "default.system.name", comment: "Default placeholder name for a system")
carouselStep = 0
}
private func createSystem() {
isFieldFocused = false
let trimmed = systemName.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return }
onCreate(trimmed)
}
private func advanceCarousel() {
guard imageNames.count > 1 else { return }
let next = carouselStep + 1
withAnimation(.easeInOut(duration: animationDuration)) {
carouselStep = next
}
if next == imageNames.count {
DispatchQueue.main.asyncAfter(deadline: .now() + animationDuration) {
withAnimation(.none) {
carouselStep = 0
}
}
}
}
}
#Preview {
SystemsOnboardingView { _ in }
}