diff --git a/action-sheet/README.md b/action-sheet/README.md
index b72498423..8641a0aa3 100644
--- a/action-sheet/README.md
+++ b/action-sheet/README.md
@@ -84,18 +84,20 @@ to select.
#### ShowActionsResult
-| Prop | Type | Description | Since |
-| ----------- | ------------------- | -------------------------------------------- | ----- |
-| **`index`** | number
| The index of the clicked option (Zero-based) | 1.0.0 |
+| Prop | Type | Description | Since |
+| -------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
+| **`index`** | number
| The index of the clicked option (Zero-based), or -1 if the sheet was canceled. On iOS, if there is a button with ActionSheetButtonStyle.Cancel, and user clicks outside the sheet, the index of the cancel option is returned | 1.0.0 |
+| **`canceled`** | boolean
| True if sheet was canceled by user; False otherwise | 6.1.0 |
#### ShowActionsOptions
-| Prop | Type | Description | Since |
-| ------------- | -------------------------------- | ------------------------------------------------------------------------ | ----- |
-| **`title`** | string
| The title of the Action Sheet. | 1.0.0 |
-| **`message`** | string
| A message to show under the title. This option is only supported on iOS. | 1.0.0 |
-| **`options`** | ActionSheetButton[]
| Options the user can choose from. | 1.0.0 |
+| Prop | Type | Description | Since |
+| ---------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
+| **`title`** | string
| The title of the Action Sheet. | 1.0.0 |
+| **`message`** | string
| A message to show under the title. This option is only supported on iOS. | 1.0.0 |
+| **`options`** | ActionSheetButton[]
| Options the user can choose from. | 1.0.0 |
+| **`cancelable`** | boolean
| If true, sheet is canceled when clicked outside; If false, it is not. By default, false. On iOS, the sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false. | 6.1.0 |
#### ActionSheetButton
diff --git a/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheet.java b/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheet.java
index 13a78f11a..75315532f 100644
--- a/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheet.java
+++ b/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheet.java
@@ -26,7 +26,9 @@ public interface OnCancelListener {
@Override
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
- this.cancelListener.onCancel();
+ if (this.cancelListener != null) {
+ this.cancelListener.onCancel();
+ }
}
private String title;
diff --git a/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheetPlugin.java b/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheetPlugin.java
index 7ca88b8e4..ddddbac3d 100644
--- a/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheetPlugin.java
+++ b/action-sheet/android/src/main/java/com/capacitorjs/plugins/actionsheet/ActionSheetPlugin.java
@@ -19,6 +19,7 @@ public class ActionSheetPlugin extends Plugin {
@PluginMethod
public void showActions(final PluginCall call) {
String title = call.getString("title");
+ boolean cancelable = Boolean.TRUE.equals(call.getBoolean("cancelable", false));
JSArray options = call.getArray("options");
if (options == null) {
call.reject("Must supply options");
@@ -39,19 +40,23 @@ public void showActions(final PluginCall call) {
}
implementation.setTitle(title);
implementation.setOptions(actionOptions);
- implementation.setCancelable(false);
- implementation.setOnSelectedListener(
- index -> {
- JSObject ret = new JSObject();
- ret.put("index", index);
- call.resolve(ret);
- implementation.dismiss();
- }
- );
+ implementation.setCancelable(cancelable);
+ if (cancelable) {
+ implementation.setOnCancelListener(() -> resolve(call, -1));
+ }
+ implementation.setOnSelectedListener(index -> resolve(call, index));
implementation.show(getActivity().getSupportFragmentManager(), "capacitorModalsActionSheet");
} catch (JSONException ex) {
Logger.error("JSON error processing an option for showActions", ex);
call.reject("JSON error processing an option for showActions", ex);
}
}
+
+ private void resolve(final PluginCall call, int selectedIndex) {
+ JSObject ret = new JSObject();
+ ret.put("index", selectedIndex);
+ ret.put("canceled", selectedIndex < 0);
+ call.resolve(ret);
+ implementation.dismiss();
+ }
}
diff --git a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift
index b36c43e2f..4c2586d54 100644
--- a/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift
+++ b/action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift
@@ -15,11 +15,13 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin {
private let implementation = ActionSheet()
@objc func showActions(_ call: CAPPluginCall) {
- let title = call.options["title"] as? String
- let message = call.options["message"] as? String
+ let title = call.getString("title")
+ let message = call.getString("message")
+ let cancelable = call.getBool("cancelable", false)
let options = call.getArray("options", JSObject.self) ?? []
var alertActions = [UIAlertAction]()
+ var forceCancelableOnClickOutside = cancelable
for (index, option) in options.enumerated() {
let style = option["style"] as? String ?? "DEFAULT"
let title = option["title"] as? String ?? ""
@@ -27,11 +29,14 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin {
if style == "DESTRUCTIVE" {
buttonStyle = .destructive
} else if style == "CANCEL" {
+ // if there's a cancel action, then it will already be cancelable when clicked outside
+ forceCancelableOnClickOutside = false
buttonStyle = .cancel
}
let action = UIAlertAction(title: title, style: buttonStyle, handler: { (_) -> Void in
call.resolve([
- "index": index
+ "index": index,
+ "canceled": false
])
})
alertActions.append(action)
@@ -40,9 +45,34 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin {
DispatchQueue.main.async { [weak self] in
if let alertController = self?.implementation.buildActionSheet(title: title, message: message, actions: alertActions) {
self?.setCenteredPopover(alertController)
- self?.bridge?.viewController?.present(alertController, animated: true, completion: nil)
+ self?.bridge?.viewController?.present(alertController, animated: true) {
+ if (forceCancelableOnClickOutside) {
+ let gestureRecognizer = TapGestureRecognizerWithClosure {
+ alertController.dismiss(animated: true, completion: nil)
+ call.resolve([
+ "index": -1,
+ "canceled": true
+ ])
+ }
+ let backroundView = alertController.view.superview?.subviews[0]
+ backroundView?.addGestureRecognizer(gestureRecognizer)
+ }
+ }
}
}
}
+
+ private final class TapGestureRecognizerWithClosure: UITapGestureRecognizer {
+ private let onTap: () -> Void
+ init(onTap: @escaping () -> Void) {
+ self.onTap = onTap
+ super.init(target: nil, action: nil)
+ self.addTarget(self, action: #selector(action))
+ }
+
+ @objc private func action() {
+ onTap()
+ }
+ }
}
diff --git a/action-sheet/src/definitions.ts b/action-sheet/src/definitions.ts
index 84dbfbaae..c236dbd50 100644
--- a/action-sheet/src/definitions.ts
+++ b/action-sheet/src/definitions.ts
@@ -21,6 +21,15 @@ export interface ShowActionsOptions {
* @since 1.0.0
*/
options: ActionSheetButton[];
+
+ /**
+ * If true, sheet is canceled when clicked outside; If false, it is not. By default, false.
+ *
+ * On iOS, the sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false.
+ *
+ * @since 6.1.0
+ */
+ cancelable?: boolean;
}
export enum ActionSheetButtonStyle {
@@ -76,11 +85,19 @@ export interface ActionSheetButton {
export interface ShowActionsResult {
/**
- * The index of the clicked option (Zero-based)
+ * The index of the clicked option (Zero-based), or -1 if the sheet was canceled.
+ *
+ * On iOS, if there is a button with ActionSheetButtonStyle.Cancel, and user clicks outside the sheet, the index of the cancel option is returned
*
* @since 1.0.0
*/
index: number;
+ /**
+ * True if sheet was canceled by user; False otherwise
+ *
+ * @since 6.1.0
+ */
+ canceled: boolean;
}
export interface ActionSheetPlugin {
diff --git a/action-sheet/src/web.ts b/action-sheet/src/web.ts
index c63e0f576..3a517c5fc 100644
--- a/action-sheet/src/web.ts
+++ b/action-sheet/src/web.ts
@@ -15,14 +15,23 @@ export class ActionSheetWeb extends WebPlugin implements ActionSheetPlugin {
document.body.appendChild(actionSheet);
}
actionSheet.header = options.title;
- actionSheet.cancelable = false;
+ actionSheet.cancelable = options.cancelable;
actionSheet.options = options.options;
actionSheet.addEventListener('onSelection', async (e: any) => {
const selection = e.detail;
resolve({
index: selection,
+ canceled: false,
});
});
+ if (options.cancelable) {
+ actionSheet.addEventListener('onCanceled', async () => {
+ resolve({
+ index: -1,
+ canceled: true,
+ });
+ });
+ }
});
}
}