Skip to content

Commit dc8e9b3

Browse files
added new api to present suAlert with an item
1 parent 18f53ba commit dc8e9b3

File tree

1 file changed

+212
-84
lines changed

1 file changed

+212
-84
lines changed
Lines changed: 212 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,133 @@
11
import SwiftUI
22

3+
struct AlertContent: View {
4+
@Binding var isPresented: Bool
5+
let model: AlertVM
6+
let primaryAction: (() -> Void)?
7+
let secondaryAction: (() -> Void)?
8+
9+
var body: some View {
10+
SUCenterModal(
11+
isVisible: self.$isPresented,
12+
model: self.model.modalVM,
13+
header: {
14+
if self.model.message.isNotNil,
15+
let text = self.model.title {
16+
self.title(text)
17+
}
18+
},
19+
body: {
20+
if let text = self.model.message {
21+
self.message(text)
22+
} else if let text = self.model.title {
23+
self.title(text)
24+
}
25+
},
26+
footer: {
27+
switch AlertButtonsOrientationCalculator.preferredOrientation(model: model) {
28+
case .horizontal:
29+
HStack(spacing: AlertVM.buttonsSpacing) {
30+
self.button(
31+
model: self.model.secondaryButtonVM,
32+
action: self.secondaryAction
33+
)
34+
self.button(
35+
model: self.model.primaryButtonVM,
36+
action: self.primaryAction
37+
)
38+
}
39+
case .vertical:
40+
VStack(spacing: AlertVM.buttonsSpacing) {
41+
self.button(
42+
model: self.model.primaryButtonVM,
43+
action: self.primaryAction
44+
)
45+
self.button(
46+
model: self.model.secondaryButtonVM,
47+
action: self.secondaryAction
48+
)
49+
}
50+
}
51+
}
52+
)
53+
}
54+
55+
// MARK: - Helpers
56+
57+
func title(_ text: String) -> some View {
58+
Text(text)
59+
.font(UniversalFont.mdHeadline.font)
60+
.foregroundStyle(UniversalColor.foreground.color)
61+
.multilineTextAlignment(.center)
62+
.frame(maxWidth: .infinity)
63+
}
64+
65+
func message(_ text: String) -> some View {
66+
Text(text)
67+
.font(UniversalFont.mdBody.font)
68+
.foregroundStyle(UniversalColor.secondaryForeground.color)
69+
.multilineTextAlignment(.center)
70+
.frame(maxWidth: .infinity)
71+
}
72+
73+
func button(
74+
model: ButtonVM?,
75+
action: (() -> Void)?
76+
) -> some View {
77+
Group {
78+
if let model {
79+
SUButton(model: model) {
80+
action?()
81+
self.isPresented = false
82+
}
83+
}
84+
}
85+
}
86+
}
87+
88+
// MARK: - Helpers
89+
90+
private struct AlertTitle: View {
91+
let text: String
92+
93+
var body: some View {
94+
Text(self.text)
95+
.font(UniversalFont.mdHeadline.font)
96+
.foregroundStyle(UniversalColor.foreground.color)
97+
.multilineTextAlignment(.center)
98+
.frame(maxWidth: .infinity)
99+
}
100+
}
101+
102+
private struct AlertMessage: View {
103+
let text: String
104+
105+
var body: some View {
106+
Text(self.text)
107+
.font(UniversalFont.mdBody.font)
108+
.foregroundStyle(UniversalColor.secondaryForeground.color)
109+
.multilineTextAlignment(.center)
110+
.frame(maxWidth: .infinity)
111+
}
112+
}
113+
114+
private struct AlertButton: View {
115+
@Binding var isAlertPresented: Bool
116+
let model: ButtonVM?
117+
let action: (() -> Void)?
118+
119+
var body: some View {
120+
if let model {
121+
SUButton(model: model) {
122+
self.action?()
123+
self.isAlertPresented = false
124+
}
125+
}
126+
}
127+
}
128+
129+
// MARK: - Presentation Helpers
130+
3131
extension View {
4132
/// A SwiftUI view modifier that presents an alert with a title, message, and up to two action buttons.
5133
///
@@ -53,95 +181,95 @@ extension View {
53181
transitionDuration: model.transition.value,
54182
onDismiss: onDismiss,
55183
content: {
56-
SUCenterModal(
57-
isVisible: isPresented,
58-
model: model.modalVM,
59-
header: {
60-
if model.message.isNotNil,
61-
let title = model.title {
62-
AlertTitle(text: title)
63-
}
64-
},
65-
body: {
66-
if let message = model.message {
67-
AlertMessage(text: message)
68-
} else if let title = model.title {
69-
AlertTitle(text: title)
70-
}
71-
},
72-
footer: {
73-
switch AlertButtonsOrientationCalculator.preferredOrientation(model: model) {
74-
case .horizontal:
75-
HStack(spacing: AlertVM.buttonsSpacing) {
76-
AlertButton(
77-
isAlertPresented: isPresented,
78-
model: model.secondaryButtonVM,
79-
action: secondaryAction
80-
)
81-
AlertButton(
82-
isAlertPresented: isPresented,
83-
model: model.primaryButtonVM,
84-
action: primaryAction
85-
)
86-
}
87-
case .vertical:
88-
VStack(spacing: AlertVM.buttonsSpacing) {
89-
AlertButton(
90-
isAlertPresented: isPresented,
91-
model: model.primaryButtonVM,
92-
action: primaryAction
93-
)
94-
AlertButton(
95-
isAlertPresented: isPresented,
96-
model: model.secondaryButtonVM,
97-
action: secondaryAction
98-
)
99-
}
100-
}
101-
}
184+
AlertContent(
185+
isPresented: isPresented,
186+
model: model,
187+
primaryAction: primaryAction,
188+
secondaryAction: secondaryAction
102189
)
103190
}
104191
)
105192
}
106-
}
107-
108-
// MARK: - Helpers
109-
110-
private struct AlertTitle: View {
111-
let text: String
112-
113-
var body: some View {
114-
Text(self.text)
115-
.font(UniversalFont.mdHeadline.font)
116-
.foregroundStyle(UniversalColor.foreground.color)
117-
.multilineTextAlignment(.center)
118-
.frame(maxWidth: .infinity)
119-
}
120-
}
121-
122-
private struct AlertMessage: View {
123-
let text: String
124-
125-
var body: some View {
126-
Text(self.text)
127-
.font(UniversalFont.mdBody.font)
128-
.foregroundStyle(UniversalColor.secondaryForeground.color)
129-
.multilineTextAlignment(.center)
130-
.frame(maxWidth: .infinity)
131-
}
132-
}
133-
134-
private struct AlertButton: View {
135-
@Binding var isAlertPresented: Bool
136-
let model: ButtonVM?
137-
let action: (() -> Void)?
138193

139-
var body: some View {
140-
if let model {
141-
SUButton(model: model) {
142-
self.action?()
143-
self.isAlertPresented = false
194+
/// A SwiftUI view modifier that presents an alert with a title, message, and up to two action buttons.
195+
///
196+
/// All actions in an alert dismiss the alert after the action runs. If no actions are present, a standard “OK” action is included.
197+
///
198+
/// - Parameters:
199+
/// - isPresented: A binding that determines whether the alert is presented.
200+
/// - item: A binding to an optional `Item` that determines whether the alert is presented.
201+
/// When `item` is `nil`, the alert is hidden.
202+
/// - primaryAction: An optional closure executed when the primary button is tapped.
203+
/// - secondaryAction: An optional closure executed when the secondary button is tapped.
204+
/// - onDismiss: An optional closure executed when the alert is dismissed.
205+
///
206+
/// - Example:
207+
/// ```swift
208+
/// struct ContentView: View {
209+
/// struct AlertData: Identifiable {
210+
/// var id: String {
211+
/// return text
212+
/// }
213+
/// let text: String
214+
/// }
215+
///
216+
/// @State private var selectedItem: AlertData?
217+
/// private let items: [AlertData] = [
218+
/// AlertData(text: "data 1"),
219+
/// AlertData(text: "data 2")
220+
/// ]
221+
///
222+
/// var body: some View {
223+
/// List(items) { item in
224+
/// Button("Show Alert") {
225+
/// selectedItem = item
226+
/// }
227+
/// }
228+
/// .suAlert(
229+
/// item: $selectedItem,
230+
/// model: { data in
231+
/// return AlertVM {
232+
/// $0.title = "Data Preview"
233+
/// $0.message = data.text
234+
/// }
235+
/// },
236+
/// onDismiss: {
237+
/// print("Alert dismissed")
238+
/// }
239+
/// )
240+
/// }
241+
/// }
242+
/// ```
243+
public func suAlert<Item: Identifiable>(
244+
item: Binding<Item?>,
245+
model: @escaping (Item) -> AlertVM,
246+
primaryAction: ((Item) -> Void)? = nil,
247+
secondaryAction: ((Item) -> Void)? = nil,
248+
onDismiss: (() -> Void)? = nil
249+
) -> some View {
250+
return self.modal(
251+
item: item,
252+
transitionDuration: { model($0).transition.value },
253+
onDismiss: onDismiss,
254+
content: { unwrappedItem in
255+
AlertContent(
256+
isPresented: .init(
257+
get: {
258+
return item.wrappedValue.isNotNil
259+
},
260+
set: { isPresented in
261+
if isPresented {
262+
item.wrappedValue = unwrappedItem
263+
} else {
264+
item.wrappedValue = nil
265+
}
266+
}
267+
),
268+
model: model(unwrappedItem),
269+
primaryAction: { primaryAction?(unwrappedItem) },
270+
secondaryAction: { secondaryAction?(unwrappedItem) }
271+
)
144272
}
145-
}
273+
)
146274
}
147275
}

0 commit comments

Comments
 (0)