@@ -90,6 +90,45 @@ struct SUBottomModal<Header: View, Body: View, Footer: View>: View {
9090// MARK: - Presentation Helpers
9191
9292extension View {
93+ /// A SwiftUI view modifier that presents a bottom-aligned modal.
94+ ///
95+ /// This modifier allows you to attach a bottom modal to any SwiftUI view, providing a structured way to display modals
96+ /// with a header, body, and footer, all styled and laid out according to the provided `BottomModalVM` model.
97+ ///
98+ /// - Parameters:
99+ /// - isPresented: A binding that determines whether the modal is presented.
100+ /// - model: A model that defines the appearance properties.
101+ /// - onDismiss: An optional closure executed when the modal is dismissed.
102+ /// - header: A closure that provides the view for the modal's header.
103+ /// - body: A closure that provides the view for the modal's main content.
104+ /// - footer: A closure that provides the view for the modal's footer.
105+ ///
106+ /// - Returns: A modified `View` with a bottom modal attached.
107+ ///
108+ /// - Example:
109+ /// ```swift
110+ /// SomeView()
111+ /// .bottomModal(
112+ /// isPresented: $isModalPresented,
113+ /// model: BottomModalVM(),
114+ /// onDismiss: {
115+ /// print("Modal dismissed")
116+ /// },
117+ /// header: {
118+ /// Text("Header")
119+ /// },
120+ /// body: {
121+ /// Text("Body content goes here")
122+ /// },
123+ /// footer: {
124+ /// SUButton(model: .init {
125+ /// $0.title = "Close"
126+ /// }) {
127+ /// isModalPresented = false
128+ /// }
129+ /// }
130+ /// )
131+ /// ```
93132 public func bottomModal< Header: View , Body: View , Footer: View > (
94133 isPresented: Binding < Bool > ,
95134 model: BottomModalVM = . init( ) ,
@@ -116,6 +155,67 @@ extension View {
116155}
117156
118157extension View {
158+ /// A SwiftUI view modifier that presents a bottom-aligned modal bound to an optional identifiable item.
159+ ///
160+ /// This modifier allows you to attach a modal to any SwiftUI view, which is displayed when the `item` binding
161+ /// is non-`nil`. The modal content is dynamically generated based on the unwrapped `Item`.
162+ ///
163+ /// - Parameters:
164+ /// - item: A binding to an optional `Item` that determines whether the modal is presented.
165+ /// When `item` is `nil`, the modal is hidden.
166+ /// - model: A model that defines the appearance properties.
167+ /// - onDismiss: An optional closure executed when the modal is dismissed. Defaults to `nil`.
168+ /// - header: A closure that provides the view for the modal's header, based on the unwrapped `Item`.
169+ /// - body: A closure that provides the view for the modal's main content, based on the unwrapped `Item`.
170+ /// - footer: A closure that provides the view for the modal's footer, based on the unwrapped `Item`.
171+ ///
172+ /// - Returns: A modified `View` with a bottom modal attached.
173+ ///
174+ /// - Example:
175+ /// ```swift
176+ /// struct ContentView: View {
177+ /// struct ModalData: Identifiable {
178+ /// var id: String {
179+ /// return text
180+ /// }
181+ /// let text: String
182+ /// }
183+ ///
184+ /// @State private var selectedItem: ModalData?
185+ /// private let items: [ModalData] = [
186+ /// ModalData(text: "data 1"),
187+ /// ModalData(text: "data 2")
188+ /// ]
189+ ///
190+ /// var body: some View {
191+ /// List(items) { item in
192+ /// Button("Show Modal") {
193+ /// selectedItem = item
194+ /// }
195+ /// }
196+ /// .bottomModal(
197+ /// item: $selectedItem,
198+ /// model: BottomModalVM(),
199+ /// onDismiss: {
200+ /// print("Modal dismissed")
201+ /// },
202+ /// header: { item in
203+ /// Text("Header for \(item.text)")
204+ /// },
205+ /// body: { item in
206+ /// Text("Body content for \(item.text)")
207+ /// },
208+ /// footer: { _ in
209+ /// SUButton(model: .init {
210+ /// $0.title = "Close"
211+ /// }) {
212+ /// selectedItem = nil
213+ /// }
214+ /// }
215+ /// )
216+ /// }
217+ /// }
218+ /// ```
119219 public func bottomModal< Item: Identifiable , Header: View , Body: View , Footer: View > (
120220 item: Binding < Item ? > ,
121221 model: BottomModalVM = . init( ) ,
@@ -151,6 +251,55 @@ extension View {
151251 )
152252 }
153253
254+ /// A SwiftUI view modifier that presents a bottom-aligned modal bound to an optional identifiable item.
255+ ///
256+ /// This modifier allows you to attach a modal to any SwiftUI view, which is displayed when the `item` binding
257+ /// is non-`nil`. The modal content is dynamically generated based on the unwrapped `Item`.
258+ ///
259+ /// - Parameters:
260+ /// - item: A binding to an optional `Item` that determines whether the modal is presented.
261+ /// When `item` is `nil`, the modal is hidden.
262+ /// - model: A model that defines the appearance properties.
263+ /// - onDismiss: An optional closure executed when the modal is dismissed. Defaults to `nil`.
264+ /// - body: A closure that provides the view for the modal's main content, based on the unwrapped `Item`.
265+ ///
266+ /// - Returns: A modified `View` with a bottom modal attached.
267+ ///
268+ /// - Example:
269+ /// ```swift
270+ /// struct ContentView: View {
271+ /// struct ModalData: Identifiable {
272+ /// var id: String {
273+ /// return text
274+ /// }
275+ /// let text: String
276+ /// }
277+ ///
278+ /// @State private var selectedItem: ModalData?
279+ /// private let items: [ModalData] = [
280+ /// ModalData(text: "data 1"),
281+ /// ModalData(text: "data 2")
282+ /// ]
283+ ///
284+ /// var body: some View {
285+ /// List(items) { item in
286+ /// Button("Show Modal") {
287+ /// selectedItem = item
288+ /// }
289+ /// }
290+ /// .bottomModal(
291+ /// item: $selectedItem,
292+ /// model: BottomModalVM(),
293+ /// onDismiss: {
294+ /// print("Modal dismissed")
295+ /// },
296+ /// body: { item in
297+ /// Text("Body content for \(item.text)")
298+ /// }
299+ /// )
300+ /// }
301+ /// }
302+ /// ```
154303 public func bottomModal< Item: Identifiable , Body: View > (
155304 item: Binding < Item ? > ,
156305 model: BottomModalVM = . init( ) ,
0 commit comments