1 contributor
70 lines | 2.29kb
//
//  MeterInfoCardView.swift
//  USB Meter
//

import SwiftUI

struct MeterInfoCardView<Content: View, TrailingActions: View>: View {
    let title: String
    let infoMessage: String?
    let tint: Color
    let isCollapsible: Bool
    @State private var isExpanded: Bool
    @ViewBuilder var trailingActions: TrailingActions
    @ViewBuilder var content: Content

    init(
        title: String,
        infoMessage: String? = nil,
        tint: Color,
        isCollapsible: Bool = false,
        initiallyExpanded: Bool = true,
        @ViewBuilder trailingActions: () -> TrailingActions = { EmptyView() },
        @ViewBuilder content: () -> Content
    ) {
        self.title = title
        self.infoMessage = infoMessage
        self.tint = tint
        self.isCollapsible = isCollapsible
        self._isExpanded = State(initialValue: initiallyExpanded)
        self.trailingActions = trailingActions()
        self.content = content()
    }

    var body: some View {
        VStack(alignment: .leading, spacing: isExpanded ? 12 : 0) {
            HStack(spacing: 8) {
                Text(title)
                    .font(.headline)
                if let infoMessage {
                    ContextInfoButton(title: title, message: infoMessage)
                }
                Spacer(minLength: 0)
                trailingActions
                if isCollapsible {
                    Image(systemName: "chevron.up")
                        .font(.caption.weight(.semibold))
                        .foregroundColor(.secondary)
                        .rotationEffect(.degrees(isExpanded ? 0 : -180))
                        .animation(.easeInOut(duration: 0.2), value: isExpanded)
                }
            }
            .contentShape(Rectangle())
            .onTapGesture {
                guard isCollapsible else { return }
                withAnimation(.easeInOut(duration: 0.25)) {
                    isExpanded.toggle()
                }
            }

            if isExpanded {
                content
                    .transition(.opacity.combined(with: .move(edge: .top)))
            }
        }
        .frame(maxWidth: .infinity, alignment: .leading)
        .padding(18)
        .meterCard(tint: tint, fillOpacity: 0.18, strokeOpacity: 0.24)
    }
}