import SwiftUI
import SwiftPopup

struct ExamplePopupView: View, PopupViewProtocol {
    var title: String = "Example Popup"
    
    var body: some View {
        Text("This is the content of the popup")
    }
}

struct ExamplePopupView2: View, PopupViewProtocol {
    var title: String = "Example Popup 2"
    
    var body: some View {
        VStack {
            ForEach(0..<11) { _ in
                Text("This is the content of the popup 2")
            }
        }
    }
}

struct PackageTest: View {
    @StateObject private var popupManager = PopupManager()
    @State private var isPopupVisible = false
    
    @State private var count = 0
    
    @Namespace private var ButtonAnimation
    
    var body: some View {
        VStack {
            Spacer()
            
            Text("Counter: \(count)") // Displays the counter value
                .font(.largeTitle)
                .padding()
            
            HStack {
                Button(action: {
                    count -= 1 // Decrement the counter
                }) {
                    Text("-")
                        .font(.largeTitle)
                        .padding()
                        .background(Color.red)
                        .foregroundColor(.white)
                        .clipShape(Circle())
                }
                
                Button(action: {
                    count += 1 // Increment the counter
                }) {
                    Text("+")
                        .font(.largeTitle)
                        .padding()
                        .background(Color.green)
                        .foregroundColor(.white)
                        .clipShape(Circle())
                }
            }
            .padding()
            
            Text("\(popupManager.isPopupPresented)")

            Spacer()
            
                        
            Button("Show Popup") {
                popupManager.setSequence(
                    ExamplePopupView(),
                    ExamplePopupView2(),
                    ExamplePopupView()
                )
                isPopupVisible = true
                popupManager.showPopup()
            }
            .matchedGeometryEffect(id: "ShapeButton", in: ButtonAnimation, anchor: .bottom)
            .buttonStyle(PrimaryButtonStyle(controlSize: .large, buttonType: .primary, fullWidth: true))
//            .geometryGroup()
            .disabled(popupManager.isPopupPresented) // Disable the button while popup is presented
            .zIndex(4)
        }
        .padding(EdgeInsets(top: 0, leading: 24, bottom: 0, trailing: 24))
        .popup(popupManager: popupManager) { title in
            // Custom header
            HStack {
                Text(title)
                    .font(.title2)
                    .fontWeight(.bold)
                    .foregroundStyle(.white)
                
                Spacer()
                
                Button(action: {
                    popupManager.dismissPopupWithAnimation()
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                        isPopupVisible = false
                    }
                }) {
                    Image(systemName: "xmark")
                        .font(.system(size: 13, weight: .bold))
                        .foregroundStyle(.white)
                        .padding(7)
                        .background(.white.opacity(0.4))
                        .clipShape(Circle())
                }
            }
            .padding(24)
            .background(Color.blue)
        } footer: {
            // Custom footer
            HStack {
                Button {
                    popupManager.navigateNext()
                } label: {
                    Text("Go Next")
                }
                .matchedGeometryEffect(id: "ShapeButton", in: ButtonAnimation, anchor: .bottom)
                .buttonStyle(PrimaryButtonStyle(controlSize: .large, buttonType: .primary, fullWidth: true))
            }
        } styles: {
            PopupStyles(
                padding: 0,
                headerStyle: .init(
                    padding: 0
                ),
                contentStyle: .init(
                    padding: 24
                ),
                footerStyle: .init(
                    padding: .init(top: 0, leading: 24, bottom: 24, trailing: 24)
                )
            )
        }
    }
}



#Preview {
    PackageTest()
}



import SwiftUI

public enum Constants {
    public static let ANIMATION_DURATION: Double = 0.28
    public static let ANIMATION: Animation = .snappy(duration: ANIMATION_DURATION)
}


import SwiftUI

public struct PopupContainer<Header: View, Footer: View>: View {
    @ObservedObject private var popupManager: PopupManager
    private let header: (String) -> Header
    private let footer: () -> Footer
    
    private let styles: PopupStyles
    
    @State private var initialAnimationCompleted = false
    @State private var offset: CGSize = .zero
    @State private var backgroundOpacity: Double = 0

    public init(
        popupManager: PopupManager,
        @ViewBuilder header: @escaping (String) -> Header,
        @ViewBuilder footer: @escaping () -> Footer,
        styles: PopupStyles
    ) {
        self.popupManager = popupManager
        self.header = header
        self.footer = footer
        self.styles = styles
    }
    
    public var body: some View {
        GeometryReader { geometry in
            ZStack {
                Color(.systemFill)
                    .opacity(backgroundOpacity)
                    .animation(Constants.ANIMATION, value: popupManager.isPopupPresented)
                    .onTapGesture {
                        popupManager.dismissPopupWithAnimation()
                    }
                    .ignoresSafeArea()
                
            VStack {
                    Spacer()
                    
                    VStack {
                        /// HEADER
                        HStack {
                            if let lastView = popupManager.viewStack.last {
                                header(lastView.title)
                                    .id(lastView.id)
                                    .transition(.opacity)
                                    .clipShape(
                                        UnevenRoundedRectangle(cornerRadii: .init(topLeading: styles.cornerRadius, topTrailing: styles.cornerRadius), style: .continuous)
                                    )
                            }
                        }
                        .geometryGroup()
                        .frame(maxWidth: .infinity, alignment: .leading)
                        .padding(styles.headerStyle?.padding ?? .init(top:0, leading: 0, bottom: 0, trailing: 0))
                        
                        /// BODY
                        VStack {
                            if let lastView = popupManager.viewStack.last?.view {
                                lastView
                                    .transition(popupManager.viewTransition)
                            }
                        }
                        .padding(styles.contentStyle?.padding ?? .init(top:0, leading: 0, bottom: 0, trailing: 0))
                        .clipped()
                        .geometryGroup()
                        .frame(maxWidth: .infinity, alignment: .leading)
                        
                        
                        /// FOOTER
                        footer()
                            .frame(maxWidth: .infinity, alignment: .leading)
                            .padding(styles.footerStyle?.padding ?? .init(top:0, leading: 0, bottom: 0, trailing: 0))
                        
                    }
                    .padding(styles.padding)
                    .frame(maxWidth: .greatestFiniteMagnitude)
                    .background(
                        RoundedRectangle(cornerRadius: styles.cornerRadius, style: .continuous)
                            .fill(styles.backgroundColor)
                            .strokeBorder(.secondary.opacity(0.2), lineWidth: 1)
                    )
                    .padding(geometry.safeAreaInsets.bottom / 3)
                    .clipped()
                    .clipShape(
                        RoundedRectangle(cornerRadius: styles.cornerRadius, style: .continuous)
                    )
                    .containerRelativeFrame(.vertical, alignment: .bottom)
                    .offset(y: withAnimation(.smooth()) {
                        popupManager.isPopupPresented && initialAnimationCompleted ? offset.height : geometry.size.height + offset.height
                    })
//                    .animation(Constants.ANIMATION.speed(popupManager.isPopupPresented ? 1 : 1), value: popupManager.isPopupPresented)
                }
                .gesture(
                    DragGesture()
                        .onChanged { value in
                            withAnimation(Constants.ANIMATION) {
                                if value.translation.height > 0 {
                                    offset = value.translation
                                    backgroundOpacity = 1 - Double(offset.height / 200)
                                }
                            }
                        }
                        .onEnded { value in
                            if value.translation.height > 100 {
                                popupManager.dismissPopupWithAnimation()
                                DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                                    offset = .zero
                                    backgroundOpacity = 1
                                }
                            } else {
                                withAnimation(.bouncy(duration: Constants.ANIMATION_DURATION)) {
                                    offset = .zero
                                    backgroundOpacity = 1
                                }
                            }
                        }
                )
            }
        }
        .onAppear {
            withAnimation(Constants.ANIMATION.speed(1)) {
                backgroundOpacity = 1
            }
            withAnimation(Constants.ANIMATION) {
                initialAnimationCompleted = true
            }
        }    }
}


import SwiftUI

@MainActor
public class PopupManager: ObservableObject {
    @Published public var isPopupPresented: Bool = false
    @Published public var isForward: Bool = false
    @Published public private(set) var viewStack: [(id: String, view: AnyView, title: String)] = []
    @Published public private(set) var viewTransition: AnyTransition = .identity
    
    @Published public private(set) var isFirstInStack: Bool = true
    @Published public private(set) var isLastInStack: Bool = true
    
    private var sequenceStack: [(id: String, view: AnyView, title: String)] = []
    private var currentSequenceIndex: Int = 0
    
    public init() {}
    
    private func transitionForDirection() -> AnyTransition {
        self.isForward ?
            .asymmetric(
                insertion: .move(edge: .trailing).combined(with: .blurReplaceC),
                removal: .move(edge: .leading).combined(with: .blurReplaceC)
            ) :
            .asymmetric(
                insertion: .move(edge: .leading).combined(with: .blurReplaceC),
                removal: .move(edge: .trailing).combined(with: .blurReplaceC)
            )
    }
    
    private func resetTransition() {
        viewTransition = .opacity
    }
    
    private func setTransition() {
        viewTransition = .scale(scale: 0.9).combined(with: .blurReplaceC)
    }
    
    public func setSequence(_ views: any PopupViewProtocol...) {
        sequenceStack = views.enumerated().map { index, view in
            let id = String(describing: type(of: view))
            return (id: id, view: AnyView(view), title: view.title)
        }
        currentSequenceIndex = 0
    }

    public func showPopup() {
        guard !sequenceStack.isEmpty else {
            print("Error: Sequence is empty. Call setSequence before showPopup.")
            return
        }
        
        let firstView = sequenceStack[0]
        viewStack.removeAll()
        setTransition()
        
        withAnimation(Constants.ANIMATION) {
            viewStack.append(firstView)
            isPopupPresented = true
        }
        
//        DispatchQueue.main.asyncAfter(deadline: .now() + Constants.ANIMATION_DURATION) {
//            self.resetTransition()
//        }
        
        currentSequenceIndex = 1
        updateStackPosition()
    }
    
    public func dismissPopup() {
//        withAnimation(.interactiveSpring(response: 0.1, dampingFraction: 0.97, blendDuration: 1)) {
//            isPopupPresented = false
//        }
        withAnimation(Constants.ANIMATION) {
            viewStack.removeAll()
            resetTransition()
        }
        currentSequenceIndex = 0
        updateStackPosition()
        print("Dismissed popup.")
    }
    
    public func navigateNext() {
        guard currentSequenceIndex < sequenceStack.count else {
            print("End of sequence reached.")
            return
        }
        
        let nextView = sequenceStack[currentSequenceIndex]
        
        isForward = true
        setTransition()
        withAnimation(Constants.ANIMATION.speed(0.9)) {
            viewStack.append(nextView)
        }
        
//        DispatchQueue.main.asyncAfter(deadline: .now() + Constants.ANIMATION_DURATION) {
//            self.resetTransition()
//        }
        
        currentSequenceIndex += 1
        updateStackPosition()
    }
    
    public func navigateBack() {
        guard viewStack.count > 1 else {
            dismissPopup()
            return
        }
        
        isForward = false
        setTransition()
        withAnimation(Constants.ANIMATION.speed(0.9)) {
            _ = viewStack.popLast()
        }
        
//        DispatchQueue.main.asyncAfter(deadline: .now() + Constants.ANIMATION_DURATION) {
//            self.resetTransition()
//        }
        
        currentSequenceIndex -= 1
        updateStackPosition()
    }
    
    private func updateStackPosition() {
        withAnimation(Constants.ANIMATION) {
            isFirstInStack = viewStack.count <= 1
            isLastInStack = currentSequenceIndex >= sequenceStack.count
        }
    }
    
    public func dismissPopupWithAnimation() {
            withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
                isPopupPresented = false
            }
            
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                self.dismissPopup()
            }
        }

}


import SwiftUI

public protocol PopupViewProtocol: View {
    var title: String { get }
}

public struct PopupStyles {
    // Global style to be applied across the popup
    public var backgroundColor: Color
    public var cornerRadius: CGFloat
    public var padding: EdgeInsets
    
    // Section-specific styles (optional)
    public var headerStyle: SectionStyle?
    public var contentStyle: SectionStyle?
    public var footerStyle: SectionStyle?
    
    // Initializer for uniform padding with CGFloat
    public init(
        backgroundColor: Color = .white,
        cornerRadius: CGFloat = 38,
        padding: CGFloat = 24,
        headerStyle: SectionStyle? = nil,
        contentStyle: SectionStyle? = nil,
        footerStyle: SectionStyle? = nil
    ) {
        self.backgroundColor = backgroundColor
        self.cornerRadius = cornerRadius
        // Convert CGFloat to EdgeInsets for uniform padding
        self.padding = EdgeInsets(top: padding, leading: padding, bottom: padding, trailing: padding)
        self.headerStyle = headerStyle
        self.contentStyle = contentStyle
        self.footerStyle = footerStyle
    }
    
    // Initializer for EdgeInsets
    public init(
        backgroundColor: Color = .white,
        cornerRadius: CGFloat = 38,
        padding: EdgeInsets,
        headerStyle: SectionStyle? = nil,
        contentStyle: SectionStyle? = nil,
        footerStyle: SectionStyle? = nil
    ) {
        self.backgroundColor = backgroundColor
        self.cornerRadius = cornerRadius
        self.padding = padding
        self.headerStyle = headerStyle
        self.contentStyle = contentStyle
        self.footerStyle = footerStyle
    }
}

public struct SectionStyle {
//    public var backgroundColor: Color
//    public var cornerRadius: CGFloat
    public var padding: EdgeInsets
    
    // Initializer for uniform padding with CGFloat
    public init(
//        backgroundColor: Color = .clear,
//        cornerRadius: CGFloat = 0,
        padding: CGFloat = 24
    ) {
//        self.backgroundColor = backgroundColor
//        self.cornerRadius = cornerRadius
        // Convert CGFloat to EdgeInsets for uniform padding
        self.padding = EdgeInsets(top: padding, leading: padding, bottom: padding, trailing: padding)
    }
    
    // Initializer for EdgeInsets
    public init(
//        backgroundColor: Color = .clear,
//        cornerRadius: CGFloat = 0,
        padding: EdgeInsets
    ) {
//        self.backgroundColor = backgroundColor
//        self.cornerRadius = cornerRadius
        self.padding = padding
    }
}

public struct SwiftPopup<Header: View, Footer: View>: ViewModifier {
    @ObservedObject var popupManager: PopupManager
    let header: (String) -> Header
    let footer: () -> Footer
    let stylesProvider: PopupStyles
    
    @State private var isAnimating: Bool = false
    
    public init(
        popupManager: PopupManager,
        @ViewBuilder header: @escaping (String) -> Header,
        @ViewBuilder footer: @escaping () -> Footer,
        styles: @escaping () -> PopupStyles = { PopupStyles() }
    ) {
        self.popupManager = popupManager
        self.header = header
        self.footer = footer
        self.stylesProvider = styles()
    }
    
    public func body(content: Content) -> some View {
        ZStack {
            content
                .zIndex(2)
            
            if popupManager.isPopupPresented || isAnimating {
                ZStack {
                    PopupContainer(popupManager: popupManager, header: header, footer: footer, styles: stylesProvider)
//                        .transition(.opacity)
                        .onChange(of: popupManager.isPopupPresented) { newValue in
                            print("Popup state changed")
                            if newValue {
                                print("Popup appeared")
                                withAnimation {
                                    self.isAnimating = true
                                }
                            } else {
                                print("Popup disappeared")
                                withAnimation {
                                    self.isAnimating = false
                                }
                            }
                        }
                        .opacity(popupManager.isPopupPresented ? 1 : 0)
                }
                .zIndex(2)
            }
            
//            if popupManager.isPopupPresented || isAnimating {
//                ZStack {
//                    Color.black.opacity(popupManager.isPopupPresented ? 0.4 : 0) // Optional backdrop
//                        .edgesIgnoringSafeArea(.all)
//                        .onTapGesture {
//                            withAnimation {
//                                popupManager.isPopupPresented = false
//                            }
//                        }
//                    
//                    PopupContainer(popupManager: popupManager, header: header, footer: footer, styles: stylesProvider)
//                        .transition(.opacity)
//                        .onAppear {
//                            withAnimation(.easeIn(duration: 0.25)) {
//                                self.isAnimating = true
//                            }
//                        }
//                        .onDisappear {
//                            withAnimation(.easeOut(duration: 0.25)) {
//                                self.isAnimating = false
//                            }
//                        }
//                        .opacity(popupManager.isPopupPresented ? 1 : 0)
//                }
//                .zIndex(2)
//            }
        }
    }
}


public extension View {
    func popup<Header: View, Footer: View>(
        popupManager: PopupManager,
        @ViewBuilder header: @escaping (String) -> Header,
        @ViewBuilder footer: @escaping () -> Footer,
        styles: @escaping () -> PopupStyles = { PopupStyles() }
    ) -> some View {
        self.modifier(SwiftPopup(popupManager: popupManager, header: header, footer: footer, styles: styles))
    }
}


Fix the error and make it modular if possible. What do you think of existing implementation? .blur replacec in Transitions file is empty - create it .