130 lines
4.7 KiB
Swift
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 }
|
|
}
|