react-native-unified-action-sheet 0.1.1 → 0.3.0
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/CHANGELOG.md +66 -1
- package/README.md +38 -105
- package/android/build.gradle +28 -1
- package/android/src/main/java/com/unifiedactionsheet/ActionSheetOptions.kt +6 -2
- package/android/src/main/java/com/unifiedactionsheet/PromptOptions.kt +132 -0
- package/android/src/main/java/com/unifiedactionsheet/SheetContentBuilder.kt +31 -0
- package/android/src/main/java/com/unifiedactionsheet/SheetPresenters.kt +54 -0
- package/android/src/main/java/com/unifiedactionsheet/UnifiedActionSheetModule.kt +61 -0
- package/android/src/material/java/com/unifiedactionsheet/BottomSheetPresenterFactory.kt +10 -0
- package/android/src/material/java/com/unifiedactionsheet/MaterialBottomSheetPresenter.kt +90 -0
- package/android/src/material/res/values/unified_action_sheet_bottom_sheet.xml +22 -0
- package/android/src/noMaterial/java/com/unifiedactionsheet/BottomSheetPresenterFactory.kt +10 -0
- package/app.plugin.js +1 -0
- package/ios/BottomSheetViewController.swift +208 -0
- package/ios/UnifiedActionSheet.mm +73 -1
- package/ios/UnifiedActionSheetImpl.swift +248 -11
- package/jest/index.d.ts +15 -1
- package/jest/index.js +28 -0
- package/lib/commonjs/NativeUnifiedActionSheet.js.map +1 -1
- package/lib/commonjs/action-sheet-options.interface.js +4 -0
- package/lib/commonjs/common-options.interface.js +2 -0
- package/lib/commonjs/common-options.interface.js.map +1 -0
- package/lib/commonjs/index.js +57 -5
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/prompt-options.interface.js +6 -0
- package/lib/commonjs/prompt-options.interface.js.map +1 -0
- package/lib/module/NativeUnifiedActionSheet.js.map +1 -1
- package/lib/module/action-sheet-options.interface.js +2 -0
- package/lib/module/common-options.interface.js +2 -0
- package/lib/module/common-options.interface.js.map +1 -0
- package/lib/module/index.js +55 -4
- package/lib/module/index.js.map +1 -1
- package/lib/module/prompt-options.interface.js +4 -0
- package/lib/module/prompt-options.interface.js.map +1 -0
- package/lib/typescript/commonjs/src/NativeUnifiedActionSheet.d.ts +23 -0
- package/lib/typescript/commonjs/src/NativeUnifiedActionSheet.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/action-sheet-options.interface.d.ts +5 -14
- package/lib/typescript/commonjs/src/action-sheet-options.interface.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/common-options.interface.d.ts +17 -0
- package/lib/typescript/commonjs/src/common-options.interface.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/index.d.ts +4 -0
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/prompt-options.interface.d.ts +19 -0
- package/lib/typescript/commonjs/src/prompt-options.interface.d.ts.map +1 -0
- package/lib/typescript/module/src/NativeUnifiedActionSheet.d.ts +23 -0
- package/lib/typescript/module/src/NativeUnifiedActionSheet.d.ts.map +1 -1
- package/lib/typescript/module/src/action-sheet-options.interface.d.ts +5 -14
- package/lib/typescript/module/src/action-sheet-options.interface.d.ts.map +1 -1
- package/lib/typescript/module/src/common-options.interface.d.ts +17 -0
- package/lib/typescript/module/src/common-options.interface.d.ts.map +1 -0
- package/lib/typescript/module/src/index.d.ts +4 -0
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/prompt-options.interface.d.ts +19 -0
- package/lib/typescript/module/src/prompt-options.interface.d.ts.map +1 -0
- package/package.json +12 -2
- package/plugin/withUnifiedActionSheet.js +58 -0
- package/src/NativeUnifiedActionSheet.ts +22 -0
- package/src/action-sheet-options.interface.ts +10 -20
- package/src/common-options.interface.ts +18 -0
- package/src/index.tsx +100 -6
- package/src/prompt-options.interface.ts +27 -0
|
@@ -2,7 +2,9 @@ import Foundation
|
|
|
2
2
|
import UIKit
|
|
3
3
|
|
|
4
4
|
@objc(UnifiedActionSheetImpl)
|
|
5
|
-
public class UnifiedActionSheetImpl: NSObject, UIPopoverPresentationControllerDelegate
|
|
5
|
+
public class UnifiedActionSheetImpl: NSObject, UIPopoverPresentationControllerDelegate,
|
|
6
|
+
UISheetPresentationControllerDelegate
|
|
7
|
+
{
|
|
6
8
|
@objc public static let shared = UnifiedActionSheetImpl()
|
|
7
9
|
|
|
8
10
|
private static let dismissedByApi = -2
|
|
@@ -31,6 +33,28 @@ public class UnifiedActionSheetImpl: NSObject, UIPopoverPresentationControllerDe
|
|
|
31
33
|
return
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
// 'bottom' is a real sheet on iPhone. iPad keeps the action sheet below,
|
|
37
|
+
// which presents as a popover there: a sheet on iPad is a centered form
|
|
38
|
+
// sheet, not attached to the bottom at all.
|
|
39
|
+
if options["presentationStyle"] as? String == "bottom",
|
|
40
|
+
UIDevice.current.userInterfaceIdiom != .pad
|
|
41
|
+
{
|
|
42
|
+
presentBottomSheet(
|
|
43
|
+
on: parent,
|
|
44
|
+
options: options,
|
|
45
|
+
labels: labels,
|
|
46
|
+
cancelButtonIndex: cancelButtonIndex,
|
|
47
|
+
destructiveIndices: destructiveIndices,
|
|
48
|
+
disabledIndices: disabledIndices,
|
|
49
|
+
tintColor: tintColor,
|
|
50
|
+
cancelButtonTintColor: cancelButtonTintColor,
|
|
51
|
+
destructiveColor: destructiveColor,
|
|
52
|
+
completion: completion
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
34
58
|
// 'centered' maps to UIKit's centered alert style; anything else (including
|
|
35
59
|
// absent, and 'anchored') stays .actionSheet, the iOS default — presented
|
|
36
60
|
// from the bottom on iPhone and as a popover on iPad.
|
|
@@ -45,7 +69,7 @@ public class UnifiedActionSheetImpl: NSObject, UIPopoverPresentationControllerDe
|
|
|
45
69
|
let presentation = Presentation(
|
|
46
70
|
controller: alert,
|
|
47
71
|
cancelButtonIndex: cancelButtonIndex,
|
|
48
|
-
completion: completion
|
|
72
|
+
completion: { index, _ in completion(index) }
|
|
49
73
|
)
|
|
50
74
|
|
|
51
75
|
for (index, label) in labels.enumerated() {
|
|
@@ -111,6 +135,183 @@ public class UnifiedActionSheetImpl: NSObject, UIPopoverPresentationControllerDe
|
|
|
111
135
|
parent.present(alert, animated: true)
|
|
112
136
|
}
|
|
113
137
|
|
|
138
|
+
/// React Native's Alert.prompt is iOS-only; this is the shared half of the
|
|
139
|
+
/// unified prompt. Always .alert -- UIKit has no text field in an action
|
|
140
|
+
/// sheet, and a prompt is inherently a centered, modal question.
|
|
141
|
+
@objc public func showPrompt(
|
|
142
|
+
options: NSDictionary,
|
|
143
|
+
completion: @escaping (Int, String) -> Void
|
|
144
|
+
) {
|
|
145
|
+
let labels = options["options"] as? [String] ?? []
|
|
146
|
+
let cancelButtonIndex = (options["cancelButtonIndex"] as? NSNumber)?.intValue ?? -1
|
|
147
|
+
let destructiveIndices = Set(
|
|
148
|
+
(options["destructiveButtonIndices"] as? [NSNumber])?.map { $0.intValue } ?? []
|
|
149
|
+
)
|
|
150
|
+
let disabledIndices = Set(
|
|
151
|
+
(options["disabledButtonIndices"] as? [NSNumber])?.map { $0.intValue } ?? []
|
|
152
|
+
)
|
|
153
|
+
let tintColor = Self.color(options["tintColor"])
|
|
154
|
+
let cancelButtonTintColor = Self.color(options["cancelButtonTintColor"])
|
|
155
|
+
let destructiveColor = Self.color(options["destructiveColor"])
|
|
156
|
+
|
|
157
|
+
guard let parent = Self.presentedViewController() else {
|
|
158
|
+
completion(cancelButtonIndex, "")
|
|
159
|
+
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let alert = UIAlertController(
|
|
164
|
+
title: Self.text(options["title"]),
|
|
165
|
+
message: Self.text(options["message"]),
|
|
166
|
+
preferredStyle: .alert
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
alert.addTextField { field in
|
|
170
|
+
field.placeholder = Self.text(options["placeholder"])
|
|
171
|
+
field.text = Self.text(options["defaultValue"])
|
|
172
|
+
field.isSecureTextEntry = (options["secureTextEntry"] as? NSNumber)?.boolValue ?? false
|
|
173
|
+
field.keyboardType = Self.keyboardType(options["keyboardType"])
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let presentation = Presentation(
|
|
177
|
+
controller: alert,
|
|
178
|
+
cancelButtonIndex: cancelButtonIndex,
|
|
179
|
+
// Weak, or the presentation would retain the controller it is stored on.
|
|
180
|
+
currentText: { [weak alert] in alert?.textFields?.first?.text ?? "" },
|
|
181
|
+
completion: completion
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
for (index, label) in labels.enumerated() {
|
|
185
|
+
let isCancel = index == cancelButtonIndex
|
|
186
|
+
let style: UIAlertAction.Style =
|
|
187
|
+
destructiveIndices.contains(index) ? .destructive : (isCancel ? .cancel : .default)
|
|
188
|
+
|
|
189
|
+
let action = UIAlertAction(title: label, style: style) { [weak self] _ in
|
|
190
|
+
self?.finish(presentation, index: index)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let color: UIColor? =
|
|
194
|
+
style == .destructive
|
|
195
|
+
? destructiveColor
|
|
196
|
+
: (isCancel ? (cancelButtonTintColor ?? tintColor) : tintColor)
|
|
197
|
+
|
|
198
|
+
action.isEnabled = !disabledIndices.contains(index)
|
|
199
|
+
|
|
200
|
+
if let color {
|
|
201
|
+
action.setValue(color, forKey: "titleTextColor")
|
|
202
|
+
}
|
|
203
|
+
alert.addAction(action)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
alert.view.tintColor = tintColor
|
|
207
|
+
|
|
208
|
+
switch options["userInterfaceStyle"] as? String {
|
|
209
|
+
case "dark": alert.overrideUserInterfaceStyle = .dark
|
|
210
|
+
case "light": alert.overrideUserInterfaceStyle = .light
|
|
211
|
+
default: alert.overrideUserInterfaceStyle = .unspecified
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
presentations.append(presentation)
|
|
215
|
+
parent.present(alert, animated: true)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private func presentBottomSheet(
|
|
219
|
+
on parent: UIViewController,
|
|
220
|
+
options: NSDictionary,
|
|
221
|
+
labels: [String],
|
|
222
|
+
cancelButtonIndex: Int,
|
|
223
|
+
destructiveIndices: Set<Int>,
|
|
224
|
+
disabledIndices: Set<Int>,
|
|
225
|
+
tintColor: UIColor?,
|
|
226
|
+
cancelButtonTintColor: UIColor?,
|
|
227
|
+
destructiveColor: UIColor?,
|
|
228
|
+
completion: @escaping (Int) -> Void
|
|
229
|
+
) {
|
|
230
|
+
let rows = labels.enumerated().map { index, label in
|
|
231
|
+
BottomSheetViewController.Row(
|
|
232
|
+
index: index,
|
|
233
|
+
label: label,
|
|
234
|
+
isDestructive: destructiveIndices.contains(index),
|
|
235
|
+
isEnabled: !disabledIndices.contains(index)
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
let sheet = BottomSheetViewController(
|
|
240
|
+
title: Self.text(options["title"]),
|
|
241
|
+
message: Self.text(options["message"]),
|
|
242
|
+
rows: rows.filter { $0.index != cancelButtonIndex },
|
|
243
|
+
cancelRow: rows.first { $0.index == cancelButtonIndex },
|
|
244
|
+
tintColor: tintColor,
|
|
245
|
+
cancelButtonTintColor: cancelButtonTintColor,
|
|
246
|
+
destructiveColor: destructiveColor
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
let presentation = Presentation(
|
|
250
|
+
controller: sheet,
|
|
251
|
+
cancelButtonIndex: cancelButtonIndex,
|
|
252
|
+
completion: { index, _ in completion(index) }
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
// Weak: the presentation holds the sheet, which holds this closure.
|
|
256
|
+
sheet.onSelect = { [weak self, weak presentation] index in
|
|
257
|
+
guard let presentation else { return }
|
|
258
|
+
|
|
259
|
+
// Unlike an alert action, a row does not dismiss the sheet itself.
|
|
260
|
+
// Resolve once it is gone, as an alert does.
|
|
261
|
+
presentation.controller.dismiss(animated: true) {
|
|
262
|
+
self?.finish(presentation, index: index)
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
switch options["userInterfaceStyle"] as? String {
|
|
267
|
+
case "dark": sheet.overrideUserInterfaceStyle = .dark
|
|
268
|
+
case "light": sheet.overrideUserInterfaceStyle = .light
|
|
269
|
+
default: sheet.overrideUserInterfaceStyle = .unspecified
|
|
270
|
+
}
|
|
271
|
+
sheet.view.tintColor = tintColor
|
|
272
|
+
|
|
273
|
+
if let controller = sheet.sheetPresentationController {
|
|
274
|
+
controller.prefersGrabberVisible = true
|
|
275
|
+
controller.prefersEdgeAttachedInCompactHeight = true
|
|
276
|
+
// Swipe down resolves through presentationControllerDidDismiss.
|
|
277
|
+
controller.delegate = self
|
|
278
|
+
|
|
279
|
+
let bounds = parent.view.window?.bounds ?? parent.view.bounds
|
|
280
|
+
let fitting = sheet.fittingHeight(width: bounds.width)
|
|
281
|
+
|
|
282
|
+
// A short list opens at its own height; a long one opens at half height
|
|
283
|
+
// and drags up to expand, like the Android sheet. Fitting the content
|
|
284
|
+
// needs a custom detent, iOS 16+; iOS 15 opens at half height.
|
|
285
|
+
if #available(iOS 16.0, *), fitting <= bounds.height / 2 {
|
|
286
|
+
controller.detents = [
|
|
287
|
+
.custom(identifier: .init("unifiedActionSheet.content")) { context in
|
|
288
|
+
min(fitting, context.maximumDetentValue)
|
|
289
|
+
},
|
|
290
|
+
]
|
|
291
|
+
} else {
|
|
292
|
+
controller.detents = [.medium(), .large()]
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
presentations.append(presentation)
|
|
297
|
+
parent.present(sheet, animated: true)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/// Interactive dismissal of a sheet: a swipe down or a tap on the dimmed
|
|
301
|
+
/// area. It resolves the cancel index, or -1 without one, like Android. A
|
|
302
|
+
/// programmatic dismiss() does not call this.
|
|
303
|
+
public func presentationControllerDidDismiss(
|
|
304
|
+
_ presentationController: UIPresentationController
|
|
305
|
+
) {
|
|
306
|
+
guard
|
|
307
|
+
let presentation = presentations.first(where: {
|
|
308
|
+
$0.controller === presentationController.presentedViewController
|
|
309
|
+
})
|
|
310
|
+
else { return }
|
|
311
|
+
|
|
312
|
+
finish(presentation, index: presentation.cancelButtonIndex)
|
|
313
|
+
}
|
|
314
|
+
|
|
114
315
|
@objc public func dismissAll() {
|
|
115
316
|
guard let bottom = presentations.first else { return }
|
|
116
317
|
|
|
@@ -131,6 +332,21 @@ public class UnifiedActionSheetImpl: NSObject, UIPopoverPresentationControllerDe
|
|
|
131
332
|
}
|
|
132
333
|
}
|
|
133
334
|
|
|
335
|
+
/// The JS runtime is going away: close every sheet, prompt and alert, but
|
|
336
|
+
/// resolve none of them. Their promises belong to the runtime being torn
|
|
337
|
+
/// down, and the list is shared, so stale entries must not outlive it.
|
|
338
|
+
@objc public func invalidate() {
|
|
339
|
+
let all = presentations
|
|
340
|
+
presentations.removeAll()
|
|
341
|
+
all.forEach { $0.discard() }
|
|
342
|
+
|
|
343
|
+
// As in dismissAll(): the lowest sheet's presenter dismisses it and
|
|
344
|
+
// everything stacked above it.
|
|
345
|
+
all.first { $0.controller.presentingViewController != nil }?
|
|
346
|
+
.controller.presentingViewController?
|
|
347
|
+
.dismiss(animated: false)
|
|
348
|
+
}
|
|
349
|
+
|
|
134
350
|
@objc public func dismiss() {
|
|
135
351
|
guard let presentation = presentations.last else { return }
|
|
136
352
|
|
|
@@ -144,10 +360,8 @@ public class UnifiedActionSheetImpl: NSObject, UIPopoverPresentationControllerDe
|
|
|
144
360
|
public func popoverPresentationControllerDidDismissPopover(
|
|
145
361
|
_ popoverPresentationController: UIPopoverPresentationController
|
|
146
362
|
) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
as? UIAlertController,
|
|
150
|
-
let presentation = presentations.first(where: { $0.controller === alert })
|
|
363
|
+
let alert = popoverPresentationController.presentedViewController
|
|
364
|
+
guard let presentation = presentations.first(where: { $0.controller === alert })
|
|
151
365
|
else { return }
|
|
152
366
|
|
|
153
367
|
finish(presentation, index: presentation.cancelButtonIndex)
|
|
@@ -161,25 +375,36 @@ public class UnifiedActionSheetImpl: NSObject, UIPopoverPresentationControllerDe
|
|
|
161
375
|
}
|
|
162
376
|
|
|
163
377
|
private final class Presentation {
|
|
164
|
-
|
|
378
|
+
/// A UIAlertController, or the bottom sheet on iPhone.
|
|
379
|
+
let controller: UIViewController
|
|
165
380
|
let cancelButtonIndex: Int
|
|
166
|
-
|
|
381
|
+
/// Read at resolve time, not at creation: the value that matters is
|
|
382
|
+
/// whatever is in the field when the prompt closes. Sheets pass a constant.
|
|
383
|
+
private let currentText: () -> String
|
|
384
|
+
private var completion: ((Int, String) -> Void)?
|
|
167
385
|
|
|
168
386
|
init(
|
|
169
|
-
controller:
|
|
387
|
+
controller: UIViewController,
|
|
170
388
|
cancelButtonIndex: Int,
|
|
171
|
-
|
|
389
|
+
currentText: @escaping () -> String = { "" },
|
|
390
|
+
completion: @escaping (Int, String) -> Void
|
|
172
391
|
) {
|
|
173
392
|
self.controller = controller
|
|
174
393
|
self.cancelButtonIndex = cancelButtonIndex
|
|
394
|
+
self.currentText = currentText
|
|
175
395
|
self.completion = completion
|
|
176
396
|
}
|
|
177
397
|
|
|
398
|
+
/// Drops the completion without calling it.
|
|
399
|
+
func discard() {
|
|
400
|
+
completion = nil
|
|
401
|
+
}
|
|
402
|
+
|
|
178
403
|
func resolve(_ index: Int) {
|
|
179
404
|
guard let completion else { return }
|
|
180
405
|
|
|
181
406
|
self.completion = nil
|
|
182
|
-
completion(index)
|
|
407
|
+
completion(index, currentText())
|
|
183
408
|
}
|
|
184
409
|
}
|
|
185
410
|
|
|
@@ -208,6 +433,18 @@ public class UnifiedActionSheetImpl: NSObject, UIPopoverPresentationControllerDe
|
|
|
208
433
|
return CGRect(x: x, y: y, width: width, height: height)
|
|
209
434
|
}
|
|
210
435
|
|
|
436
|
+
/// Mirrors the subset of React Native's keyboardType values that map cleanly
|
|
437
|
+
/// onto both platforms; anything else falls back to the default keyboard.
|
|
438
|
+
private static func keyboardType(_ value: Any?) -> UIKeyboardType {
|
|
439
|
+
switch value as? String {
|
|
440
|
+
case "email-address": return .emailAddress
|
|
441
|
+
case "numeric": return .numberPad
|
|
442
|
+
case "phone-pad": return .phonePad
|
|
443
|
+
case "url": return .URL
|
|
444
|
+
default: return .default
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
211
448
|
private static func text(_ value: Any?) -> String? {
|
|
212
449
|
guard let string = value as? String, !string.isEmpty else { return nil }
|
|
213
450
|
|
package/jest/index.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ActionSheetOptionsInterface,
|
|
3
|
+
PromptOptionsInterface,
|
|
4
|
+
PromptResultInterface,
|
|
5
|
+
} from 'react-native-unified-action-sheet';
|
|
2
6
|
|
|
3
7
|
/// Queues the index the next sheet resolves with. Pass nothing to go back to
|
|
4
8
|
/// resolving with no selection.
|
|
@@ -8,5 +12,15 @@ export declare const showActionSheetWithOptions: jest.Mock<
|
|
|
8
12
|
Promise<number | undefined>,
|
|
9
13
|
[ActionSheetOptionsInterface]
|
|
10
14
|
>;
|
|
15
|
+
/// Queues what the next prompt resolves with. Pass nothing to go back to
|
|
16
|
+
/// resolving with no selection.
|
|
17
|
+
export declare function setNextPromptResult(
|
|
18
|
+
result?: PromptResultInterface
|
|
19
|
+
): void;
|
|
20
|
+
|
|
21
|
+
export declare const showPromptWithOptions: jest.Mock<
|
|
22
|
+
Promise<PromptResultInterface | undefined>,
|
|
23
|
+
[PromptOptionsInterface]
|
|
24
|
+
>;
|
|
11
25
|
export declare const dismissActionSheet: jest.Mock<void, []>;
|
|
12
26
|
export declare const dismissAllActionSheets: jest.Mock<void, []>;
|
package/jest/index.js
CHANGED
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
*
|
|
17
17
|
* Prefer that over mockResolvedValueOnce, which replaces the implementation and
|
|
18
18
|
* so skips onPress.
|
|
19
|
+
*
|
|
20
|
+
* A prompt is queued the same way, with the text the field should resolve with:
|
|
21
|
+
*
|
|
22
|
+
* setNextPromptResult({ buttonIndex: 0, text: 'typed' });
|
|
23
|
+
* await openThePrompt();
|
|
19
24
|
*/
|
|
20
25
|
let nextButtonIndex;
|
|
21
26
|
|
|
@@ -35,12 +40,35 @@ const showActionSheetWithOptions = jest.fn((options) => {
|
|
|
35
40
|
return Promise.resolve(buttonIndex);
|
|
36
41
|
});
|
|
37
42
|
|
|
43
|
+
let nextPromptResult;
|
|
44
|
+
|
|
45
|
+
const setNextPromptResult = (result) => {
|
|
46
|
+
nextPromptResult = result;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const showPromptWithOptions = jest.fn((options) => {
|
|
50
|
+
const result = nextPromptResult;
|
|
51
|
+
nextPromptResult = undefined;
|
|
52
|
+
|
|
53
|
+
if (result) {
|
|
54
|
+
const button =
|
|
55
|
+
options && options.options && options.options[result.buttonIndex];
|
|
56
|
+
if (button && typeof button.onPress === 'function') {
|
|
57
|
+
button.onPress(result.text);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return Promise.resolve(result);
|
|
62
|
+
});
|
|
63
|
+
|
|
38
64
|
const dismissActionSheet = jest.fn();
|
|
39
65
|
const dismissAllActionSheets = jest.fn();
|
|
40
66
|
|
|
41
67
|
module.exports = {
|
|
42
68
|
setNextButtonIndex,
|
|
69
|
+
setNextPromptResult,
|
|
43
70
|
showActionSheetWithOptions,
|
|
71
|
+
showPromptWithOptions,
|
|
44
72
|
dismissActionSheet,
|
|
45
73
|
dismissAllActionSheets,
|
|
46
74
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","_default","exports","default","TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeUnifiedActionSheet.ts"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAAqE,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_default","exports","default","TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeUnifiedActionSheet.ts"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAAqE,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,GAkDtDC,gCAAmB,CAACC,YAAY,CAAO,oBAAoB,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../src","sources":["common-options.interface.ts"],"mappings":"","ignoreList":[]}
|
package/lib/commonjs/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.showActionSheetWithOptions = exports.dismissAllActionSheets = exports.dismissActionSheet = void 0;
|
|
6
|
+
exports.showPromptWithOptions = exports.showActionSheetWithOptions = exports.dismissAllActionSheets = exports.dismissActionSheet = void 0;
|
|
7
7
|
var _reactNative = require("react-native");
|
|
8
8
|
const DISMISSED_BY_API = -2;
|
|
9
9
|
|
|
@@ -20,12 +20,18 @@ const toWireOptions = ({
|
|
|
20
20
|
// crosses the bridge. Omit<> would not strip it at runtime.
|
|
21
21
|
anchor: _anchor,
|
|
22
22
|
...rest
|
|
23
|
-
}) => {
|
|
23
|
+
}) => ({
|
|
24
|
+
...rest,
|
|
25
|
+
...toWireButtons(options)
|
|
26
|
+
});
|
|
27
|
+
/// Same flattening as the sheet: labels plus index sets. Kept generic over the
|
|
28
|
+
/// button shape so the two APIs cannot disagree about what 'cancel' means.
|
|
29
|
+
const toWireButtons = buttons => {
|
|
24
30
|
const labels = [];
|
|
25
31
|
const destructive = [];
|
|
26
32
|
const disabled = [];
|
|
27
33
|
let cancelButtonIndex;
|
|
28
|
-
|
|
34
|
+
buttons.forEach((button, index) => {
|
|
29
35
|
labels.push(button.label);
|
|
30
36
|
if (button.style === 'destructive') destructive.push(index);
|
|
31
37
|
if (button.style === 'cancel' && cancelButtonIndex == null) {
|
|
@@ -34,7 +40,6 @@ const toWireOptions = ({
|
|
|
34
40
|
if (button.disabled) disabled.push(index);
|
|
35
41
|
});
|
|
36
42
|
return {
|
|
37
|
-
...rest,
|
|
38
43
|
options: labels,
|
|
39
44
|
...(cancelButtonIndex == null ? {} : {
|
|
40
45
|
cancelButtonIndex
|
|
@@ -50,6 +55,18 @@ const toWireOptions = ({
|
|
|
50
55
|
const showWithNativeModule = options => nativeModule().showActionSheetWithOptions(options)
|
|
51
56
|
// A programmatic dismiss resolves with no index rather than a selection.
|
|
52
57
|
.then(buttonIndex => buttonIndex === DISMISSED_BY_API ? undefined : buttonIndex).catch(() => options.cancelButtonIndex ?? -1);
|
|
58
|
+
let warnedMaterialDisabled = false;
|
|
59
|
+
|
|
60
|
+
/// 'bottom' needs Material on Android, which the app opts into at build time.
|
|
61
|
+
/// Without it the sheet falls back to a centered dialog; say so once, in
|
|
62
|
+
/// development, since the fallback alone looks like the option was ignored.
|
|
63
|
+
const warnIfBottomUnavailable = presentationStyle => {
|
|
64
|
+
if (!__DEV__ || warnedMaterialDisabled || _reactNative.Platform.OS !== 'android' || presentationStyle !== 'bottom' || nativeModule().getConstants().isMaterialEnabled) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
warnedMaterialDisabled = true;
|
|
68
|
+
console.warn("react-native-unified-action-sheet: presentationStyle 'bottom' needs Material on Android, " + 'so this sheet falls back to a centered dialog. Set unifiedActionSheet.material=true in ' + "android/gradle.properties, or add ['react-native-unified-action-sheet', { material: true }] " + 'to your Expo plugins, then rebuild the app.');
|
|
69
|
+
};
|
|
53
70
|
|
|
54
71
|
/// The anchor is measured in JS and sent across as a rect, so neither native
|
|
55
72
|
/// module has to resolve a view: a ref's own measureInWindow is the supported
|
|
@@ -83,7 +100,12 @@ const showActionSheetWithOptions = options => {
|
|
|
83
100
|
if (_reactNative.Platform.OS !== 'ios' && _reactNative.Platform.OS !== 'android') {
|
|
84
101
|
return Promise.resolve(undefined);
|
|
85
102
|
}
|
|
86
|
-
|
|
103
|
+
warnIfBottomUnavailable(options.presentationStyle);
|
|
104
|
+
|
|
105
|
+
// A bottom sheet has no anchor, so don't measure one: on iPad a rect would
|
|
106
|
+
// turn the sheet into an arrowed popover pointing at it.
|
|
107
|
+
const anchor = options.presentationStyle === 'bottom' ? undefined : options.anchor;
|
|
108
|
+
return withAnchorRect(wire, anchor).then(showWithNativeModule).then(press);
|
|
87
109
|
};
|
|
88
110
|
exports.showActionSheetWithOptions = showActionSheetWithOptions;
|
|
89
111
|
const dismissActionSheet = () => {
|
|
@@ -100,5 +122,35 @@ const dismissAllActionSheets = () => {
|
|
|
100
122
|
nativeModule().dismissAllActionSheets();
|
|
101
123
|
}
|
|
102
124
|
};
|
|
125
|
+
|
|
126
|
+
/// A prompt is the sheet's centered dialog with a text field: iOS presents a
|
|
127
|
+
/// UIAlertController alert with addTextField, Android the same AppCompatDialog
|
|
128
|
+
/// the centered style uses. React Native's own Alert.prompt is iOS-only and a
|
|
129
|
+
/// silent no-op on Android, which is the gap this fills.
|
|
103
130
|
exports.dismissAllActionSheets = dismissAllActionSheets;
|
|
131
|
+
const showPromptWithOptions = options => {
|
|
132
|
+
const {
|
|
133
|
+
options: buttons,
|
|
134
|
+
...rest
|
|
135
|
+
} = options;
|
|
136
|
+
const wire = {
|
|
137
|
+
...rest,
|
|
138
|
+
...toWireButtons(buttons)
|
|
139
|
+
};
|
|
140
|
+
if (_reactNative.Platform.OS !== 'ios' && _reactNative.Platform.OS !== 'android') {
|
|
141
|
+
return Promise.resolve(undefined);
|
|
142
|
+
}
|
|
143
|
+
return nativeModule().showPromptWithOptions(wire).then(result =>
|
|
144
|
+
// A programmatic dismiss resolves with no selection, matching the sheet.
|
|
145
|
+
result.buttonIndex === DISMISSED_BY_API ? undefined : result).catch(() => ({
|
|
146
|
+
buttonIndex: wire.cancelButtonIndex ?? -1,
|
|
147
|
+
text: ''
|
|
148
|
+
})).then(result => {
|
|
149
|
+
// The text goes to the handler, so a caller using onPress alone never
|
|
150
|
+
// has to read the resolved value.
|
|
151
|
+
if (result) buttons[result.buttonIndex]?.onPress?.(result.text);
|
|
152
|
+
return result;
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
exports.showPromptWithOptions = showPromptWithOptions;
|
|
104
156
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","DISMISSED_BY_API","nativeModule","default","toWireOptions","options","anchor","_anchor","rest","labels","destructive","disabled","cancelButtonIndex","forEach","button","index","push","label","style","length","destructiveButtonIndices","disabledButtonIndices","showWithNativeModule","showActionSheetWithOptions","then","buttonIndex","undefined","catch","withAnchorRect","wire","target","current","Promise","resolve","measureInWindow","x","y","width","height","measured","anchorRect","press","onPress","
|
|
1
|
+
{"version":3,"names":["_reactNative","require","DISMISSED_BY_API","nativeModule","default","toWireOptions","options","anchor","_anchor","rest","toWireButtons","buttons","labels","destructive","disabled","cancelButtonIndex","forEach","button","index","push","label","style","length","destructiveButtonIndices","disabledButtonIndices","showWithNativeModule","showActionSheetWithOptions","then","buttonIndex","undefined","catch","warnedMaterialDisabled","warnIfBottomUnavailable","presentationStyle","__DEV__","Platform","OS","getConstants","isMaterialEnabled","console","warn","withAnchorRect","wire","target","current","Promise","resolve","measureInWindow","x","y","width","height","measured","anchorRect","press","onPress","exports","dismissActionSheet","dismissAllActionSheets","showPromptWithOptions","result","text"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../src","sources":["prompt-options.interface.ts"],"mappings":"","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeUnifiedActionSheet.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeUnifiedActionSheet.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAkDpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,oBAAoB,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../src","sources":["common-options.interface.ts"],"mappings":"","ignoreList":[]}
|