Files
Cable/Cable/ComponentsOnboardingView.swift
2025-10-13 09:38:22 +02:00

123 lines
4.0 KiB
Swift

import SwiftUI
struct ComponentsOnboardingView: View {
@State private var carouselStep = 0
let onCreate: () -> Void
let onBrowse: () -> Void
private let imageNames = [
"coffee-onboarding",
"router-onboarding",
"charger-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("Add your first component")
.font(.title2.weight(.semibold))
.multilineTextAlignment(.center)
Text("Bring your system to life with components and let **Cable by VoltPlan** handle cable and fuse recommendations.")
.font(.body)
.foregroundStyle(Color.secondary)
.multilineTextAlignment(.center)
.frame(minHeight: 72)
.padding(.horizontal, 12)
}
.padding(.horizontal, 24)
Spacer()
VStack(spacing: 12) {
Button(action: createComponent) {
HStack(spacing: 8) {
Image(systemName: "plus.circle.fill")
.font(.system(size: 16))
Text("Create Component")
.font(.headline.weight(.semibold))
}
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.frame(height: 48)
.background(Color.blue)
.cornerRadius(12)
}
.accessibilityIdentifier("create-component-button")
.buttonStyle(.plain)
Button(action: onBrowse) {
HStack(spacing: 8) {
Image(systemName: "books.vertical")
.font(.system(size: 16))
Text("Browse Library")
.font(.headline.weight(.semibold))
}
.foregroundColor(.blue)
.frame(maxWidth: .infinity)
.frame(height: 48)
.background(
RoundedRectangle(cornerRadius: 12, style: .continuous)
.fill(Color.blue.opacity(0.12))
)
.overlay(
RoundedRectangle(cornerRadius: 12, style: .continuous)
.stroke(Color.blue.opacity(0.24), lineWidth: 1)
)
}
.accessibilityIdentifier("select-component-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() {
carouselStep = 0
}
private func createComponent() {
onCreate()
}
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 {
ComponentsOnboardingView(onCreate: {}, onBrowse: {})
}