capacitor-extended-dialog 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CapacitorExtendedDialog.podspec +17 -0
- package/Package.swift +28 -0
- package/README.md +190 -0
- package/android/build.gradle +59 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/albermonte/extendeddialog/DialogStyleOptions.java +99 -0
- package/android/src/main/java/com/albermonte/extendeddialog/ExtendedDialog.java +734 -0
- package/android/src/main/java/com/albermonte/extendeddialog/ExtendedDialogPlugin.java +207 -0
- package/android/src/main/java/com/albermonte/extendeddialog/FullScreenDialogFragment.java +589 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/android/src/main/res/values/themes.xml +34 -0
- package/dist/docs.json +384 -0
- package/dist/esm/definitions.d.ts +101 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +14 -0
- package/dist/esm/web.js +193 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +207 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +210 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/ExtendedDialogPlugin/ExtendedDialog.swift +497 -0
- package/ios/Sources/ExtendedDialogPlugin/ExtendedDialogPlugin.swift +177 -0
- package/ios/Sources/ExtendedDialogPlugin/FullScreenDialogViewController.swift +506 -0
- package/ios/Tests/ExtendedDialogPluginTests/ExtendedDialogPluginTests.swift +15 -0
- package/package.json +80 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
public struct SelectOption {
|
|
5
|
+
let label: String
|
|
6
|
+
let value: String
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
public struct DialogStyleOptions {
|
|
10
|
+
var buttonColor: UIColor?
|
|
11
|
+
var cancelButtonColor: UIColor?
|
|
12
|
+
var titleColor: UIColor?
|
|
13
|
+
var messageColor: UIColor?
|
|
14
|
+
var backgroundColor: UIColor?
|
|
15
|
+
var titleFontSize: CGFloat?
|
|
16
|
+
var messageFontSize: CGFloat?
|
|
17
|
+
var buttonFontSize: CGFloat?
|
|
18
|
+
|
|
19
|
+
init(
|
|
20
|
+
buttonColor: String? = nil,
|
|
21
|
+
cancelButtonColor: String? = nil,
|
|
22
|
+
titleColor: String? = nil,
|
|
23
|
+
messageColor: String? = nil,
|
|
24
|
+
backgroundColor: String? = nil,
|
|
25
|
+
titleFontSize: Double? = nil,
|
|
26
|
+
messageFontSize: Double? = nil,
|
|
27
|
+
buttonFontSize: Double? = nil
|
|
28
|
+
) {
|
|
29
|
+
self.buttonColor = Self.parseColor(buttonColor)
|
|
30
|
+
self.cancelButtonColor = Self.parseColor(cancelButtonColor)
|
|
31
|
+
self.titleColor = Self.parseColor(titleColor)
|
|
32
|
+
self.messageColor = Self.parseColor(messageColor)
|
|
33
|
+
self.backgroundColor = Self.parseColor(backgroundColor)
|
|
34
|
+
self.titleFontSize = titleFontSize.map { CGFloat($0) }
|
|
35
|
+
self.messageFontSize = messageFontSize.map { CGFloat($0) }
|
|
36
|
+
self.buttonFontSize = buttonFontSize.map { CGFloat($0) }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private static func parseColor(_ hex: String?) -> UIColor? {
|
|
40
|
+
guard let hex = hex, !hex.isEmpty else { return nil }
|
|
41
|
+
var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
42
|
+
hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")
|
|
43
|
+
|
|
44
|
+
var rgb: UInt64 = 0
|
|
45
|
+
guard Scanner(string: hexSanitized).scanHexInt64(&rgb) else { return nil }
|
|
46
|
+
|
|
47
|
+
if hexSanitized.count == 6 {
|
|
48
|
+
return UIColor(
|
|
49
|
+
red: CGFloat((rgb & 0xFF0000) >> 16) / 255.0,
|
|
50
|
+
green: CGFloat((rgb & 0x00FF00) >> 8) / 255.0,
|
|
51
|
+
blue: CGFloat(rgb & 0x0000FF) / 255.0,
|
|
52
|
+
alpha: 1.0
|
|
53
|
+
)
|
|
54
|
+
} else if hexSanitized.count == 8 {
|
|
55
|
+
return UIColor(
|
|
56
|
+
red: CGFloat((rgb & 0xFF000000) >> 24) / 255.0,
|
|
57
|
+
green: CGFloat((rgb & 0x00FF0000) >> 16) / 255.0,
|
|
58
|
+
blue: CGFloat((rgb & 0x0000FF00) >> 8) / 255.0,
|
|
59
|
+
alpha: CGFloat(rgb & 0x000000FF) / 255.0
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
return nil
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
var hasStyles: Bool {
|
|
66
|
+
return buttonColor != nil || cancelButtonColor != nil ||
|
|
67
|
+
titleColor != nil || messageColor != nil ||
|
|
68
|
+
backgroundColor != nil || titleFontSize != nil ||
|
|
69
|
+
messageFontSize != nil || buttonFontSize != nil
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@objc public class ExtendedDialog: NSObject {
|
|
74
|
+
|
|
75
|
+
public typealias AlertCallback = () -> Void
|
|
76
|
+
public typealias ConfirmCallback = (Bool) -> Void
|
|
77
|
+
public typealias PromptCallback = (String, Bool) -> Void
|
|
78
|
+
public typealias SingleSelectCallback = (String?, Bool) -> Void
|
|
79
|
+
public typealias MultiSelectCallback = ([String], Bool) -> Void
|
|
80
|
+
|
|
81
|
+
// MARK: - Alert
|
|
82
|
+
|
|
83
|
+
public func showAlert(
|
|
84
|
+
title: String?,
|
|
85
|
+
message: String,
|
|
86
|
+
buttonTitle: String?,
|
|
87
|
+
fullscreen: Bool,
|
|
88
|
+
styleOptions: DialogStyleOptions? = nil,
|
|
89
|
+
callback: @escaping AlertCallback
|
|
90
|
+
) {
|
|
91
|
+
DispatchQueue.main.async {
|
|
92
|
+
if fullscreen {
|
|
93
|
+
self.showFullScreenAlert(title: title, message: message, buttonTitle: buttonTitle, styleOptions: styleOptions, callback: callback)
|
|
94
|
+
} else {
|
|
95
|
+
self.showBasicAlert(title: title, message: message, buttonTitle: buttonTitle, styleOptions: styleOptions, callback: callback)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private func showBasicAlert(title: String?, message: String, buttonTitle: String?, styleOptions: DialogStyleOptions?, callback: @escaping AlertCallback) {
|
|
101
|
+
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
|
102
|
+
|
|
103
|
+
let action = UIAlertAction(title: buttonTitle ?? "OK", style: .default) { _ in
|
|
104
|
+
callback()
|
|
105
|
+
}
|
|
106
|
+
if let color = styleOptions?.buttonColor {
|
|
107
|
+
action.setValue(color, forKey: "titleTextColor")
|
|
108
|
+
}
|
|
109
|
+
alert.addAction(action)
|
|
110
|
+
|
|
111
|
+
applyLiquidGlassStyle(to: alert)
|
|
112
|
+
applyMessageStyle(to: alert, styleOptions: styleOptions)
|
|
113
|
+
presentAlert(alert)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private func showFullScreenAlert(title: String?, message: String, buttonTitle: String?, styleOptions: DialogStyleOptions?, callback: @escaping AlertCallback) {
|
|
117
|
+
let vc = FullScreenDialogViewController(
|
|
118
|
+
dialogType: .alert,
|
|
119
|
+
dialogTitle: title,
|
|
120
|
+
message: message,
|
|
121
|
+
okButtonTitle: buttonTitle ?? "OK",
|
|
122
|
+
cancelButtonTitle: nil,
|
|
123
|
+
inputPlaceholder: nil,
|
|
124
|
+
inputText: nil,
|
|
125
|
+
options: nil,
|
|
126
|
+
selectedValue: nil,
|
|
127
|
+
selectedValues: nil,
|
|
128
|
+
styleOptions: styleOptions
|
|
129
|
+
)
|
|
130
|
+
vc.alertCallback = callback
|
|
131
|
+
presentFullScreen(vc)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// MARK: - Confirm
|
|
135
|
+
|
|
136
|
+
public func showConfirm(
|
|
137
|
+
title: String?,
|
|
138
|
+
message: String,
|
|
139
|
+
okButtonTitle: String?,
|
|
140
|
+
cancelButtonTitle: String?,
|
|
141
|
+
fullscreen: Bool,
|
|
142
|
+
styleOptions: DialogStyleOptions? = nil,
|
|
143
|
+
callback: @escaping ConfirmCallback
|
|
144
|
+
) {
|
|
145
|
+
DispatchQueue.main.async {
|
|
146
|
+
if fullscreen {
|
|
147
|
+
self.showFullScreenConfirm(title: title, message: message, okButtonTitle: okButtonTitle, cancelButtonTitle: cancelButtonTitle, styleOptions: styleOptions, callback: callback)
|
|
148
|
+
} else {
|
|
149
|
+
self.showBasicConfirm(title: title, message: message, okButtonTitle: okButtonTitle, cancelButtonTitle: cancelButtonTitle, styleOptions: styleOptions, callback: callback)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private func showBasicConfirm(title: String?, message: String, okButtonTitle: String?, cancelButtonTitle: String?, styleOptions: DialogStyleOptions?, callback: @escaping ConfirmCallback) {
|
|
155
|
+
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
|
156
|
+
|
|
157
|
+
let cancelAction = UIAlertAction(title: cancelButtonTitle ?? "Cancel", style: .cancel) { _ in
|
|
158
|
+
callback(false)
|
|
159
|
+
}
|
|
160
|
+
let okAction = UIAlertAction(title: okButtonTitle ?? "OK", style: .default) { _ in
|
|
161
|
+
callback(true)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if let color = styleOptions?.cancelButtonColor {
|
|
165
|
+
cancelAction.setValue(color, forKey: "titleTextColor")
|
|
166
|
+
}
|
|
167
|
+
if let color = styleOptions?.buttonColor {
|
|
168
|
+
okAction.setValue(color, forKey: "titleTextColor")
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
alert.addAction(cancelAction)
|
|
172
|
+
alert.addAction(okAction)
|
|
173
|
+
|
|
174
|
+
applyLiquidGlassStyle(to: alert)
|
|
175
|
+
applyMessageStyle(to: alert, styleOptions: styleOptions)
|
|
176
|
+
presentAlert(alert)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private func showFullScreenConfirm(title: String?, message: String, okButtonTitle: String?, cancelButtonTitle: String?, styleOptions: DialogStyleOptions?, callback: @escaping ConfirmCallback) {
|
|
180
|
+
let vc = FullScreenDialogViewController(
|
|
181
|
+
dialogType: .confirm,
|
|
182
|
+
dialogTitle: title,
|
|
183
|
+
message: message,
|
|
184
|
+
okButtonTitle: okButtonTitle ?? "OK",
|
|
185
|
+
cancelButtonTitle: cancelButtonTitle ?? "Cancel",
|
|
186
|
+
inputPlaceholder: nil,
|
|
187
|
+
inputText: nil,
|
|
188
|
+
options: nil,
|
|
189
|
+
selectedValue: nil,
|
|
190
|
+
selectedValues: nil,
|
|
191
|
+
styleOptions: styleOptions
|
|
192
|
+
)
|
|
193
|
+
vc.confirmCallback = callback
|
|
194
|
+
presentFullScreen(vc)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// MARK: - Prompt
|
|
198
|
+
|
|
199
|
+
public func showPrompt(
|
|
200
|
+
title: String?,
|
|
201
|
+
message: String,
|
|
202
|
+
okButtonTitle: String?,
|
|
203
|
+
cancelButtonTitle: String?,
|
|
204
|
+
inputPlaceholder: String?,
|
|
205
|
+
inputText: String?,
|
|
206
|
+
fullscreen: Bool,
|
|
207
|
+
focusInput: Bool = false,
|
|
208
|
+
styleOptions: DialogStyleOptions? = nil,
|
|
209
|
+
callback: @escaping PromptCallback
|
|
210
|
+
) {
|
|
211
|
+
DispatchQueue.main.async {
|
|
212
|
+
if fullscreen {
|
|
213
|
+
self.showFullScreenPrompt(title: title, message: message, okButtonTitle: okButtonTitle, cancelButtonTitle: cancelButtonTitle, inputPlaceholder: inputPlaceholder, inputText: inputText, focusInput: focusInput, styleOptions: styleOptions, callback: callback)
|
|
214
|
+
} else {
|
|
215
|
+
self.showBasicPrompt(title: title, message: message, okButtonTitle: okButtonTitle, cancelButtonTitle: cancelButtonTitle, inputPlaceholder: inputPlaceholder, inputText: inputText, focusInput: focusInput, styleOptions: styleOptions, callback: callback)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private func showBasicPrompt(title: String?, message: String, okButtonTitle: String?, cancelButtonTitle: String?, inputPlaceholder: String?, inputText: String?, focusInput: Bool, styleOptions: DialogStyleOptions?, callback: @escaping PromptCallback) {
|
|
221
|
+
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
|
222
|
+
|
|
223
|
+
alert.addTextField { textField in
|
|
224
|
+
textField.placeholder = inputPlaceholder
|
|
225
|
+
textField.text = inputText
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
let cancelAction = UIAlertAction(title: cancelButtonTitle ?? "Cancel", style: .cancel) { _ in
|
|
229
|
+
callback("", true)
|
|
230
|
+
}
|
|
231
|
+
let okAction = UIAlertAction(title: okButtonTitle ?? "OK", style: .default) { _ in
|
|
232
|
+
let value = alert.textFields?.first?.text ?? ""
|
|
233
|
+
callback(value, false)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if let color = styleOptions?.cancelButtonColor {
|
|
237
|
+
cancelAction.setValue(color, forKey: "titleTextColor")
|
|
238
|
+
}
|
|
239
|
+
if let color = styleOptions?.buttonColor {
|
|
240
|
+
okAction.setValue(color, forKey: "titleTextColor")
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
alert.addAction(cancelAction)
|
|
244
|
+
alert.addAction(okAction)
|
|
245
|
+
|
|
246
|
+
applyLiquidGlassStyle(to: alert)
|
|
247
|
+
applyMessageStyle(to: alert, styleOptions: styleOptions)
|
|
248
|
+
presentAlert(alert)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private func showFullScreenPrompt(title: String?, message: String, okButtonTitle: String?, cancelButtonTitle: String?, inputPlaceholder: String?, inputText: String?, focusInput: Bool, styleOptions: DialogStyleOptions?, callback: @escaping PromptCallback) {
|
|
252
|
+
let vc = FullScreenDialogViewController(
|
|
253
|
+
dialogType: .prompt,
|
|
254
|
+
dialogTitle: title,
|
|
255
|
+
message: message,
|
|
256
|
+
okButtonTitle: okButtonTitle ?? "OK",
|
|
257
|
+
cancelButtonTitle: cancelButtonTitle ?? "Cancel",
|
|
258
|
+
inputPlaceholder: inputPlaceholder,
|
|
259
|
+
inputText: inputText,
|
|
260
|
+
options: nil,
|
|
261
|
+
selectedValue: nil,
|
|
262
|
+
selectedValues: nil,
|
|
263
|
+
focusInput: focusInput,
|
|
264
|
+
styleOptions: styleOptions
|
|
265
|
+
)
|
|
266
|
+
vc.promptCallback = callback
|
|
267
|
+
presentFullScreen(vc)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// MARK: - Single Select
|
|
271
|
+
|
|
272
|
+
public func showSingleSelect(
|
|
273
|
+
title: String?,
|
|
274
|
+
message: String,
|
|
275
|
+
options: [[String: String]],
|
|
276
|
+
selectedValue: String?,
|
|
277
|
+
okButtonTitle: String?,
|
|
278
|
+
cancelButtonTitle: String?,
|
|
279
|
+
fullscreen: Bool,
|
|
280
|
+
styleOptions: DialogStyleOptions? = nil,
|
|
281
|
+
callback: @escaping SingleSelectCallback
|
|
282
|
+
) {
|
|
283
|
+
DispatchQueue.main.async {
|
|
284
|
+
let selectOptions = options.compactMap { dict -> SelectOption? in
|
|
285
|
+
guard let label = dict["label"], let value = dict["value"] else { return nil }
|
|
286
|
+
return SelectOption(label: label, value: value)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if fullscreen {
|
|
290
|
+
self.showFullScreenSingleSelect(title: title, message: message, options: selectOptions, selectedValue: selectedValue, okButtonTitle: okButtonTitle, cancelButtonTitle: cancelButtonTitle, styleOptions: styleOptions, callback: callback)
|
|
291
|
+
} else {
|
|
292
|
+
self.showBasicSingleSelect(title: title, message: message, options: selectOptions, selectedValue: selectedValue, okButtonTitle: okButtonTitle, cancelButtonTitle: cancelButtonTitle, styleOptions: styleOptions, callback: callback)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private func showBasicSingleSelect(title: String?, message: String, options: [SelectOption], selectedValue: String?, okButtonTitle: String?, cancelButtonTitle: String?, styleOptions: DialogStyleOptions?, callback: @escaping SingleSelectCallback) {
|
|
298
|
+
let alert = UIAlertController(title: title, message: message, preferredStyle: .actionSheet)
|
|
299
|
+
|
|
300
|
+
for option in options {
|
|
301
|
+
let action = UIAlertAction(title: option.label, style: .default) { _ in
|
|
302
|
+
callback(option.value, false)
|
|
303
|
+
}
|
|
304
|
+
if option.value == selectedValue {
|
|
305
|
+
action.setValue(true, forKey: "checked")
|
|
306
|
+
}
|
|
307
|
+
if let color = styleOptions?.buttonColor {
|
|
308
|
+
action.setValue(color, forKey: "titleTextColor")
|
|
309
|
+
}
|
|
310
|
+
alert.addAction(action)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
let cancelAction = UIAlertAction(title: cancelButtonTitle ?? "Cancel", style: .cancel) { _ in
|
|
314
|
+
callback(nil, true)
|
|
315
|
+
}
|
|
316
|
+
if let color = styleOptions?.cancelButtonColor {
|
|
317
|
+
cancelAction.setValue(color, forKey: "titleTextColor")
|
|
318
|
+
}
|
|
319
|
+
alert.addAction(cancelAction)
|
|
320
|
+
|
|
321
|
+
applyLiquidGlassStyle(to: alert)
|
|
322
|
+
applyMessageStyle(to: alert, styleOptions: styleOptions)
|
|
323
|
+
presentAlert(alert)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private func showFullScreenSingleSelect(title: String?, message: String, options: [SelectOption], selectedValue: String?, okButtonTitle: String?, cancelButtonTitle: String?, styleOptions: DialogStyleOptions?, callback: @escaping SingleSelectCallback) {
|
|
327
|
+
let vc = FullScreenDialogViewController(
|
|
328
|
+
dialogType: .singleSelect,
|
|
329
|
+
dialogTitle: title,
|
|
330
|
+
message: message,
|
|
331
|
+
okButtonTitle: okButtonTitle ?? "OK",
|
|
332
|
+
cancelButtonTitle: cancelButtonTitle ?? "Cancel",
|
|
333
|
+
inputPlaceholder: nil,
|
|
334
|
+
inputText: nil,
|
|
335
|
+
options: options,
|
|
336
|
+
selectedValue: selectedValue,
|
|
337
|
+
selectedValues: nil,
|
|
338
|
+
styleOptions: styleOptions
|
|
339
|
+
)
|
|
340
|
+
vc.singleSelectCallback = callback
|
|
341
|
+
presentFullScreen(vc)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// MARK: - Multi Select
|
|
345
|
+
|
|
346
|
+
public func showMultiSelect(
|
|
347
|
+
title: String?,
|
|
348
|
+
message: String,
|
|
349
|
+
options: [[String: String]],
|
|
350
|
+
selectedValues: [String]?,
|
|
351
|
+
okButtonTitle: String?,
|
|
352
|
+
cancelButtonTitle: String?,
|
|
353
|
+
fullscreen: Bool,
|
|
354
|
+
styleOptions: DialogStyleOptions? = nil,
|
|
355
|
+
callback: @escaping MultiSelectCallback
|
|
356
|
+
) {
|
|
357
|
+
DispatchQueue.main.async {
|
|
358
|
+
let selectOptions = options.compactMap { dict -> SelectOption? in
|
|
359
|
+
guard let label = dict["label"], let value = dict["value"] else { return nil }
|
|
360
|
+
return SelectOption(label: label, value: value)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if fullscreen {
|
|
364
|
+
self.showFullScreenMultiSelect(title: title, message: message, options: selectOptions, selectedValues: selectedValues, okButtonTitle: okButtonTitle, cancelButtonTitle: cancelButtonTitle, styleOptions: styleOptions, callback: callback)
|
|
365
|
+
} else {
|
|
366
|
+
self.showBasicMultiSelect(title: title, message: message, options: selectOptions, selectedValues: selectedValues, okButtonTitle: okButtonTitle, cancelButtonTitle: cancelButtonTitle, styleOptions: styleOptions, callback: callback)
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
private func showBasicMultiSelect(title: String?, message: String, options: [SelectOption], selectedValues: [String]?, okButtonTitle: String?, cancelButtonTitle: String?, styleOptions: DialogStyleOptions?, callback: @escaping MultiSelectCallback) {
|
|
372
|
+
let vc = FullScreenDialogViewController(
|
|
373
|
+
dialogType: .multiSelect,
|
|
374
|
+
dialogTitle: title,
|
|
375
|
+
message: message,
|
|
376
|
+
okButtonTitle: okButtonTitle ?? "OK",
|
|
377
|
+
cancelButtonTitle: cancelButtonTitle ?? "Cancel",
|
|
378
|
+
inputPlaceholder: nil,
|
|
379
|
+
inputText: nil,
|
|
380
|
+
options: options,
|
|
381
|
+
selectedValue: nil,
|
|
382
|
+
selectedValues: selectedValues,
|
|
383
|
+
styleOptions: styleOptions
|
|
384
|
+
)
|
|
385
|
+
vc.multiSelectCallback = callback
|
|
386
|
+
presentBasicSheet(vc)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
private func showFullScreenMultiSelect(title: String?, message: String, options: [SelectOption], selectedValues: [String]?, okButtonTitle: String?, cancelButtonTitle: String?, styleOptions: DialogStyleOptions?, callback: @escaping MultiSelectCallback) {
|
|
390
|
+
let vc = FullScreenDialogViewController(
|
|
391
|
+
dialogType: .multiSelect,
|
|
392
|
+
dialogTitle: title,
|
|
393
|
+
message: message,
|
|
394
|
+
okButtonTitle: okButtonTitle ?? "OK",
|
|
395
|
+
cancelButtonTitle: cancelButtonTitle ?? "Cancel",
|
|
396
|
+
inputPlaceholder: nil,
|
|
397
|
+
inputText: nil,
|
|
398
|
+
options: options,
|
|
399
|
+
selectedValue: nil,
|
|
400
|
+
selectedValues: selectedValues,
|
|
401
|
+
styleOptions: styleOptions
|
|
402
|
+
)
|
|
403
|
+
vc.multiSelectCallback = callback
|
|
404
|
+
presentFullScreen(vc)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// MARK: - Helpers
|
|
408
|
+
|
|
409
|
+
private func applyLiquidGlassStyle(to alert: UIAlertController) {
|
|
410
|
+
// Apply Liquid Glass styling for iOS 26+
|
|
411
|
+
if #available(iOS 26, *) {
|
|
412
|
+
// iOS 26 automatically applies Liquid Glass styling to UIAlertController
|
|
413
|
+
// The system handles glass background effects for alerts natively
|
|
414
|
+
// Additional layer-based glass effects can be applied to custom views if needed
|
|
415
|
+
if let view = alert.view {
|
|
416
|
+
view.layer.cornerCurve = .continuous
|
|
417
|
+
}
|
|
418
|
+
} else {
|
|
419
|
+
// For older iOS versions, apply a similar visual style
|
|
420
|
+
if let view = alert.view {
|
|
421
|
+
view.layer.cornerRadius = 28.0
|
|
422
|
+
view.layer.cornerCurve = .continuous
|
|
423
|
+
view.clipsToBounds = true
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Ensure dialog appears on top
|
|
428
|
+
alert.view.layer.zPosition = CGFloat.greatestFiniteMagnitude
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
private func applyMessageStyle(to alert: UIAlertController, styleOptions: DialogStyleOptions?) {
|
|
432
|
+
guard let styleOptions = styleOptions, let fontSize = styleOptions.messageFontSize else { return }
|
|
433
|
+
|
|
434
|
+
// Access the message label using KVC (undocumented but commonly used)
|
|
435
|
+
if let message = alert.message {
|
|
436
|
+
let attributedMessage = NSMutableAttributedString(
|
|
437
|
+
string: message,
|
|
438
|
+
attributes: [
|
|
439
|
+
.font: UIFont.systemFont(ofSize: fontSize)
|
|
440
|
+
]
|
|
441
|
+
)
|
|
442
|
+
alert.setValue(attributedMessage, forKey: "attributedMessage")
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
private func presentAlert(_ alert: UIAlertController) {
|
|
447
|
+
guard let viewController = getTopViewController() else { return }
|
|
448
|
+
|
|
449
|
+
// Configure popover for iPad
|
|
450
|
+
if let popover = alert.popoverPresentationController {
|
|
451
|
+
popover.sourceView = viewController.view
|
|
452
|
+
popover.sourceRect = CGRect(x: viewController.view.bounds.midX, y: viewController.view.bounds.midY, width: 0, height: 0)
|
|
453
|
+
popover.permittedArrowDirections = []
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
viewController.present(alert, animated: true)
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
private func presentFullScreen(_ viewController: UIViewController) {
|
|
460
|
+
guard let topVC = getTopViewController() else { return }
|
|
461
|
+
|
|
462
|
+
viewController.modalPresentationStyle = .fullScreen
|
|
463
|
+
viewController.modalTransitionStyle = .coverVertical
|
|
464
|
+
topVC.present(viewController, animated: true)
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
private func presentBasicSheet(_ viewController: UIViewController) {
|
|
468
|
+
guard let topVC = getTopViewController() else { return }
|
|
469
|
+
|
|
470
|
+
viewController.modalPresentationStyle = .pageSheet
|
|
471
|
+
viewController.modalTransitionStyle = .coverVertical
|
|
472
|
+
|
|
473
|
+
if #available(iOS 15.0, *) {
|
|
474
|
+
if let sheet = viewController.sheetPresentationController {
|
|
475
|
+
sheet.detents = [.medium(), .large()]
|
|
476
|
+
sheet.prefersGrabberVisible = true
|
|
477
|
+
sheet.prefersScrollingExpandsWhenScrolledToEdge = false
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
topVC.present(viewController, animated: true)
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
private func getTopViewController() -> UIViewController? {
|
|
485
|
+
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
|
486
|
+
let window = windowScene.windows.first(where: { $0.isKeyWindow }),
|
|
487
|
+
var topController = window.rootViewController else {
|
|
488
|
+
return nil
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
while let presentedViewController = topController.presentedViewController {
|
|
492
|
+
topController = presentedViewController
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return topController
|
|
496
|
+
}
|
|
497
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
|
|
4
|
+
@objc(ExtendedDialogPlugin)
|
|
5
|
+
public class ExtendedDialogPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
6
|
+
public let identifier = "ExtendedDialogPlugin"
|
|
7
|
+
public let jsName = "ExtendedDialog"
|
|
8
|
+
public let pluginMethods: [CAPPluginMethod] = [
|
|
9
|
+
CAPPluginMethod(name: "alert", returnType: CAPPluginReturnPromise),
|
|
10
|
+
CAPPluginMethod(name: "confirm", returnType: CAPPluginReturnPromise),
|
|
11
|
+
CAPPluginMethod(name: "prompt", returnType: CAPPluginReturnPromise),
|
|
12
|
+
CAPPluginMethod(name: "singleSelect", returnType: CAPPluginReturnPromise),
|
|
13
|
+
CAPPluginMethod(name: "multiSelect", returnType: CAPPluginReturnPromise)
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
private let implementation = ExtendedDialog()
|
|
17
|
+
|
|
18
|
+
private func extractStyleOptions(_ call: CAPPluginCall) -> DialogStyleOptions {
|
|
19
|
+
return DialogStyleOptions(
|
|
20
|
+
buttonColor: call.getString("buttonColor"),
|
|
21
|
+
cancelButtonColor: call.getString("cancelButtonColor"),
|
|
22
|
+
titleColor: call.getString("titleColor"),
|
|
23
|
+
messageColor: call.getString("messageColor"),
|
|
24
|
+
backgroundColor: call.getString("backgroundColor"),
|
|
25
|
+
titleFontSize: call.getDouble("titleFontSize"),
|
|
26
|
+
messageFontSize: call.getDouble("messageFontSize"),
|
|
27
|
+
buttonFontSize: call.getDouble("buttonFontSize")
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@objc func alert(_ call: CAPPluginCall) {
|
|
32
|
+
let title = call.getString("title")
|
|
33
|
+
guard let message = call.getString("message") else {
|
|
34
|
+
call.reject("message is required")
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
let buttonTitle = call.getString("buttonTitle")
|
|
38
|
+
let mode = call.getString("mode") ?? "basic"
|
|
39
|
+
let fullscreen = mode == "fullscreen"
|
|
40
|
+
let styleOptions = extractStyleOptions(call)
|
|
41
|
+
|
|
42
|
+
implementation.showAlert(
|
|
43
|
+
title: title,
|
|
44
|
+
message: message,
|
|
45
|
+
buttonTitle: buttonTitle,
|
|
46
|
+
fullscreen: fullscreen,
|
|
47
|
+
styleOptions: styleOptions
|
|
48
|
+
) {
|
|
49
|
+
call.resolve()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@objc func confirm(_ call: CAPPluginCall) {
|
|
54
|
+
let title = call.getString("title")
|
|
55
|
+
guard let message = call.getString("message") else {
|
|
56
|
+
call.reject("message is required")
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
let okButtonTitle = call.getString("okButtonTitle")
|
|
60
|
+
let cancelButtonTitle = call.getString("cancelButtonTitle")
|
|
61
|
+
let mode = call.getString("mode") ?? "basic"
|
|
62
|
+
let fullscreen = mode == "fullscreen"
|
|
63
|
+
let styleOptions = extractStyleOptions(call)
|
|
64
|
+
|
|
65
|
+
implementation.showConfirm(
|
|
66
|
+
title: title,
|
|
67
|
+
message: message,
|
|
68
|
+
okButtonTitle: okButtonTitle,
|
|
69
|
+
cancelButtonTitle: cancelButtonTitle,
|
|
70
|
+
fullscreen: fullscreen,
|
|
71
|
+
styleOptions: styleOptions
|
|
72
|
+
) { value in
|
|
73
|
+
call.resolve(["value": value])
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@objc func prompt(_ call: CAPPluginCall) {
|
|
78
|
+
let title = call.getString("title")
|
|
79
|
+
guard let message = call.getString("message") else {
|
|
80
|
+
call.reject("message is required")
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
let okButtonTitle = call.getString("okButtonTitle")
|
|
84
|
+
let cancelButtonTitle = call.getString("cancelButtonTitle")
|
|
85
|
+
let inputPlaceholder = call.getString("inputPlaceholder")
|
|
86
|
+
let inputText = call.getString("inputText")
|
|
87
|
+
let mode = call.getString("mode") ?? "basic"
|
|
88
|
+
let fullscreen = mode == "fullscreen"
|
|
89
|
+
let focusInput = call.getBool("focusInput") ?? false
|
|
90
|
+
let styleOptions = extractStyleOptions(call)
|
|
91
|
+
|
|
92
|
+
implementation.showPrompt(
|
|
93
|
+
title: title,
|
|
94
|
+
message: message,
|
|
95
|
+
okButtonTitle: okButtonTitle,
|
|
96
|
+
cancelButtonTitle: cancelButtonTitle,
|
|
97
|
+
inputPlaceholder: inputPlaceholder,
|
|
98
|
+
inputText: inputText,
|
|
99
|
+
fullscreen: fullscreen,
|
|
100
|
+
focusInput: focusInput,
|
|
101
|
+
styleOptions: styleOptions
|
|
102
|
+
) { value, cancelled in
|
|
103
|
+
call.resolve([
|
|
104
|
+
"value": value,
|
|
105
|
+
"cancelled": cancelled
|
|
106
|
+
])
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@objc func singleSelect(_ call: CAPPluginCall) {
|
|
111
|
+
let title = call.getString("title")
|
|
112
|
+
guard let message = call.getString("message") else {
|
|
113
|
+
call.reject("message is required")
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
guard let optionsArray = call.getArray("options") as? [[String: String]] else {
|
|
117
|
+
call.reject("options is required")
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
let selectedValue = call.getString("selectedValue")
|
|
121
|
+
let okButtonTitle = call.getString("okButtonTitle")
|
|
122
|
+
let cancelButtonTitle = call.getString("cancelButtonTitle")
|
|
123
|
+
let mode = call.getString("mode") ?? "basic"
|
|
124
|
+
let fullscreen = mode == "fullscreen"
|
|
125
|
+
let styleOptions = extractStyleOptions(call)
|
|
126
|
+
|
|
127
|
+
implementation.showSingleSelect(
|
|
128
|
+
title: title,
|
|
129
|
+
message: message,
|
|
130
|
+
options: optionsArray,
|
|
131
|
+
selectedValue: selectedValue,
|
|
132
|
+
okButtonTitle: okButtonTitle,
|
|
133
|
+
cancelButtonTitle: cancelButtonTitle,
|
|
134
|
+
fullscreen: fullscreen,
|
|
135
|
+
styleOptions: styleOptions
|
|
136
|
+
) { value, cancelled in
|
|
137
|
+
call.resolve([
|
|
138
|
+
"value": value as Any,
|
|
139
|
+
"cancelled": cancelled
|
|
140
|
+
])
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@objc func multiSelect(_ call: CAPPluginCall) {
|
|
145
|
+
let title = call.getString("title")
|
|
146
|
+
guard let message = call.getString("message") else {
|
|
147
|
+
call.reject("message is required")
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
guard let optionsArray = call.getArray("options") as? [[String: String]] else {
|
|
151
|
+
call.reject("options is required")
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
let selectedValues = call.getArray("selectedValues") as? [String]
|
|
155
|
+
let okButtonTitle = call.getString("okButtonTitle")
|
|
156
|
+
let cancelButtonTitle = call.getString("cancelButtonTitle")
|
|
157
|
+
let mode = call.getString("mode") ?? "basic"
|
|
158
|
+
let fullscreen = mode == "fullscreen"
|
|
159
|
+
let styleOptions = extractStyleOptions(call)
|
|
160
|
+
|
|
161
|
+
implementation.showMultiSelect(
|
|
162
|
+
title: title,
|
|
163
|
+
message: message,
|
|
164
|
+
options: optionsArray,
|
|
165
|
+
selectedValues: selectedValues,
|
|
166
|
+
okButtonTitle: okButtonTitle,
|
|
167
|
+
cancelButtonTitle: cancelButtonTitle,
|
|
168
|
+
fullscreen: fullscreen,
|
|
169
|
+
styleOptions: styleOptions
|
|
170
|
+
) { values, cancelled in
|
|
171
|
+
call.resolve([
|
|
172
|
+
"values": values,
|
|
173
|
+
"cancelled": cancelled
|
|
174
|
+
])
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|